public inbox for [email protected]help / color / mirror / Atom feed
[PATCH v2] Add CREATE SEQUENCE AS <data type> clause 51+ messages / 2 participants [nested] [flat]
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v2] Add CREATE SEQUENCE AS <data type> clause @ 2016-09-08 16:00 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2016-09-08 16:00 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. --- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/create_sequence.sgml | 37 +++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 92 ++++++++++++++++++++++++-- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 94 +++++++++++++++++---------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/pg_proc.h | 2 +- src/include/commands/sequence.h | 6 +- src/include/pg_config_manual.h | 6 -- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 45 +++++++++---- src/test/regress/expected/sequence_1.out | 45 +++++++++---- src/test/regress/expected/updatable_views.out | 3 +- src/test/regress/sql/sequence.sql | 20 +++++- 16 files changed, 269 insertions(+), 100 deletions(-) diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325..a3a19ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 62ae379..f31b595 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,9 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</>-1 for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type + plus 1. </para> </listitem> </varlistentry> @@ -148,9 +165,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +366,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 00550eb..73a183e 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(p.data_type, null) AS character_data) AS data_type, + CAST(CASE format_type(p.data_type, null) WHEN 'bigint' THEN 64 WHEN 'integer' THEN 32 WHEN 'smallint' THEN 16 END AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(p.start_value AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index fc3a8ee..1b1e261 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -31,6 +31,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -212,6 +213,11 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 0); break; + case SEQ_COL_TYPE: + coldef->typeName = makeTypeNameFromOid(REGTYPEOID, -1); + coldef->colname = "sequence_type"; + value[i - 1] = ObjectIdGetDatum(new.sequence_type); + break; case SEQ_COL_CYCLE: coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->colname = "is_cycled"; @@ -1166,6 +1172,7 @@ static void init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence new, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1181,7 +1188,15 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1265,6 +1280,23 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) new->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("sequence data type cannot be changed"))); + + new->sequence_type = typenameTypeId(NULL, defGetTypeName(as_type)); + if (new->sequence_type != INT2OID && new->sequence_type != INT4OID && new->sequence_type != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + new->sequence_type = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1292,12 +1324,34 @@ init_params(ParseState *pstate, List *options, bool isInit, if (max_value != NULL && max_value->arg) { new->max_value = defGetInt64(max_value); + + if ((new->sequence_type == INT2OID && new->max_value > PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->max_value > PG_INT32_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || max_value != NULL) { if (new->increment_by > 0) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + { + /* ascending seq */ + if (new->sequence_type == INT2OID) + new->max_value = PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->max_value = PG_INT32_MAX; + else + new->max_value = PG_INT64_MAX; + } else new->max_value = -1; /* descending seq */ new->log_cnt = 0; @@ -1307,6 +1361,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (min_value != NULL && min_value->arg) { new->min_value = defGetInt64(min_value); + + if ((new->sequence_type == INT2OID && new->min_value < -PG_INT16_MAX) + || (new->sequence_type == INT4OID && new->min_value < -PG_INT32_MAX)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(new->sequence_type)))); + } + new->log_cnt = 0; } else if (isInit || min_value != NULL) @@ -1314,7 +1382,16 @@ init_params(ParseState *pstate, List *options, bool isInit, if (new->increment_by > 0) new->min_value = 1; /* ascending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ + { + /* descending seq */ + /* We use the _MAX constants for symmetry. */ + if (new->sequence_type == INT2OID) + new->min_value = -PG_INT16_MAX; + else if (new->sequence_type == INT4OID) + new->min_value = -PG_INT32_MAX; + else + new->min_value = -PG_INT64_MAX; + } new->log_cnt = 0; } @@ -1534,8 +1611,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[5]; - bool isnull[5]; + Datum values[6]; + bool isnull[6]; SeqTable elm; Relation seqrel; Buffer buf; @@ -1551,7 +1628,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(6, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1562,6 +1639,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1574,6 +1653,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[2] = Int64GetDatum(seq->max_value); values[3] = Int64GetDatum(seq->increment_by); values[4] = BoolGetDatum(seq->is_cycled); + values[5] = ObjectIdGetDatum(seq->sequence_type); UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1526c73..db99751 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3634,7 +3634,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7a2950e..bc68367 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -456,7 +456,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) SystemTypeName(column->typeName->typeOid == INT2OID ? "int2" : (column->typeName->typeOid == INT4OID ? "int4" : "int8")), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index ba9c276..2d5d012 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -16329,12 +16329,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); @@ -16342,41 +16342,28 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) /* Make sure we are in proper schema */ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); - - if (fout->remoteVersion >= 80400) + if (fout->remoteVersion >= 100000) + { + appendPQExpBuffer(query, + "SELECT sequence_name, " + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, sequence_type FROM %s", + fmtId(tbinfo->dobj.name)); + } + else if (fout->remoteVersion >= 80400) { appendPQExpBuffer(query, "SELECT sequence_name, " - "start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } else { appendPQExpBuffer(query, "SELECT sequence_name, " - "0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " - "cache_value, is_cycled FROM %s", - bufx, bufm, + "0 AS start_value, increment_by, max_value, min_value, " + "cache_value, is_cycled, 'bigint'::name AS sequence_type FROM %s", fmtId(tbinfo->dobj.name)); } @@ -16403,12 +16390,46 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) startv = PQgetvalue(res, 0, 1); incby = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - maxv = PQgetvalue(res, 0, 3); - if (!PQgetisnull(res, 0, 4)) - minv = PQgetvalue(res, 0, 4); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); cache = PQgetvalue(res, 0, 5); cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + seqtype = PQgetvalue(res, 0, 7); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT16_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == -PG_INT32_MAX) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, -PG_INT64_MAX); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -16432,6 +16453,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 37cbdcd..2d72232 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1965,6 +1965,7 @@ 'CREATE SEQUENCE test_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -1995,6 +1996,7 @@ 'CREATE SEQUENCE test_third_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..9c7fd42 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,26}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ )); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 392a626..54c24b7 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -32,6 +32,7 @@ typedef struct FormData_pg_sequence int64 min_value; int64 cache_value; int64 log_cnt; + Oid sequence_type; bool is_cycled; bool is_called; } FormData_pg_sequence; @@ -50,8 +51,9 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_MINVALUE 6 #define SEQ_COL_CACHE 7 #define SEQ_COL_LOG 8 -#define SEQ_COL_CYCLE 9 -#define SEQ_COL_CALLED 10 +#define SEQ_COL_TYPE 9 +#define SEQ_COL_CYCLE 10 +#define SEQ_COL_CALLED 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME #define SEQ_COL_LASTCOL SEQ_COL_CALLED diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index a2b2b61..d2b99fb 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -46,12 +46,6 @@ #define INDEX_MAX_KEYS 32 /* - * Set the upper and lower bounds of sequence values. - */ -#define SEQ_MAXVALUE PG_INT64_MAX -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* * When we don't have native spinlocks, we use semaphores to simulate them. * Decreasing this value reduces consumption of OS resources; increasing it * may improve performance, but supplying a real spinlock implementation is diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 55f0eb4..a6e0efb 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 4ffbe92..1141d5c 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 05da2bf..d5ecbf8 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS foo; +ERROR: type "foo" does not exist +ALTER SEQUENCE sequence_test3 AS bigint; -- fail +ERROR: sequence data type cannot be changed +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -173,9 +189,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | bigint | f | f (1 row) SELECT nextval('foo_seq_new'); @@ -191,9 +207,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | sequence_type | is_cycled | is_called +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+---------------+-----------+----------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | bigint | f | t (1 row) DROP SEQUENCE foo_seq_new; @@ -301,19 +317,22 @@ SELECT nextval('sequence_test2'); (1 row) -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test3 | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(6 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(10 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index f60991e..1b451e5 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -107,6 +107,7 @@ SELECT table_name, column_name, is_updatable ro_view19 | min_value | NO ro_view19 | cache_value | NO ro_view19 | log_cnt | NO + ro_view19 | sequence_type | NO ro_view19 | is_cycled | NO ro_view19 | is_called | NO ro_view2 | a | NO @@ -134,7 +135,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(46 rows) +(47 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 98a2e7d..897d918 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,18 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test3 AS integer; +CREATE SEQUENCE sequence_test4 AS smallint; +CREATE SEQUENCE sequence_test5 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS foo; + +ALTER SEQUENCE sequence_test3 AS bigint; -- fail + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -139,9 +154,8 @@ CREATE SEQUENCE sequence_test2 START WITH 32; SELECT nextval('sequence_test2'); -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; -- Test comments -- 2.10.0 --------------9E85198205691FDEAB75A291 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9E85198205691FDEAB75A291-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v4] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-25 14:35 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-25 14:35 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 52 ++++++++++---- src/test/regress/expected/sequence_1.out | 52 ++++++++++---- src/test/regress/sql/sequence.sql | 25 +++++-- 17 files changed, 312 insertions(+), 120 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 524180e011..162975746d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..cfbea6f0f8 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmax > PG_INT16_MAX) + || (seqform->seqtypid == INT4OID && seqform->seqmax > PG_INT32_MAX) + || (seqform->seqtypid == INT8OID && seqform->seqmax > PG_INT64_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmin < PG_INT16_MIN) + || (seqform->seqtypid == INT4OID && seqform->seqmin < PG_INT32_MIN) + || (seqform->seqtypid == INT8OID && seqform->seqmin < PG_INT64_MIN)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a8e35feccc..62c2bb6ff8 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c1f492b396..5ab96099ae 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 70719a36a7..250f53cffe 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index a2bdd3002b..768ba3b03f 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 5d7ab72944..ae7d834e20 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index a79330e780..a30a4933d1 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -143,16 +159,13 @@ CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; -- Test comments -- 2.11.0 --------------B52E26A5778158D3CF6AABE7 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------B52E26A5778158D3CF6AABE7-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v4] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-25 14:35 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-25 14:35 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 52 ++++++++++---- src/test/regress/expected/sequence_1.out | 52 ++++++++++---- src/test/regress/sql/sequence.sql | 25 +++++-- 17 files changed, 312 insertions(+), 120 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 524180e011..162975746d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..cfbea6f0f8 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmax > PG_INT16_MAX) + || (seqform->seqtypid == INT4OID && seqform->seqmax > PG_INT32_MAX) + || (seqform->seqtypid == INT8OID && seqform->seqmax > PG_INT64_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmin < PG_INT16_MIN) + || (seqform->seqtypid == INT4OID && seqform->seqmin < PG_INT32_MIN) + || (seqform->seqtypid == INT8OID && seqform->seqmin < PG_INT64_MIN)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a8e35feccc..62c2bb6ff8 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c1f492b396..5ab96099ae 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 70719a36a7..250f53cffe 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index a2bdd3002b..768ba3b03f 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 5d7ab72944..ae7d834e20 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index a79330e780..a30a4933d1 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -143,16 +159,13 @@ CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; -- Test comments -- 2.11.0 --------------B52E26A5778158D3CF6AABE7 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------B52E26A5778158D3CF6AABE7-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v4] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-25 14:35 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-25 14:35 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 52 ++++++++++---- src/test/regress/expected/sequence_1.out | 52 ++++++++++---- src/test/regress/sql/sequence.sql | 25 +++++-- 17 files changed, 312 insertions(+), 120 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 524180e011..162975746d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..cfbea6f0f8 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmax > PG_INT16_MAX) + || (seqform->seqtypid == INT4OID && seqform->seqmax > PG_INT32_MAX) + || (seqform->seqtypid == INT8OID && seqform->seqmax > PG_INT64_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmin < PG_INT16_MIN) + || (seqform->seqtypid == INT4OID && seqform->seqmin < PG_INT32_MIN) + || (seqform->seqtypid == INT8OID && seqform->seqmin < PG_INT64_MIN)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a8e35feccc..62c2bb6ff8 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c1f492b396..5ab96099ae 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 70719a36a7..250f53cffe 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index a2bdd3002b..768ba3b03f 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 5d7ab72944..ae7d834e20 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index a79330e780..a30a4933d1 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -143,16 +159,13 @@ CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; -- Test comments -- 2.11.0 --------------B52E26A5778158D3CF6AABE7 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------B52E26A5778158D3CF6AABE7-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v4] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-25 14:35 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-25 14:35 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 52 ++++++++++---- src/test/regress/expected/sequence_1.out | 52 ++++++++++---- src/test/regress/sql/sequence.sql | 25 +++++-- 17 files changed, 312 insertions(+), 120 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 524180e011..162975746d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..cfbea6f0f8 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmax > PG_INT16_MAX) + || (seqform->seqtypid == INT4OID && seqform->seqmax > PG_INT32_MAX) + || (seqform->seqtypid == INT8OID && seqform->seqmax > PG_INT64_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmin < PG_INT16_MIN) + || (seqform->seqtypid == INT4OID && seqform->seqmin < PG_INT32_MIN) + || (seqform->seqtypid == INT8OID && seqform->seqmin < PG_INT64_MIN)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a8e35feccc..62c2bb6ff8 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c1f492b396..5ab96099ae 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 70719a36a7..250f53cffe 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index a2bdd3002b..768ba3b03f 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 5d7ab72944..ae7d834e20 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index a79330e780..a30a4933d1 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -143,16 +159,13 @@ CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; -- Test comments -- 2.11.0 --------------B52E26A5778158D3CF6AABE7 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------B52E26A5778158D3CF6AABE7-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v4] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-25 14:35 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-25 14:35 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 52 ++++++++++---- src/test/regress/expected/sequence_1.out | 52 ++++++++++---- src/test/regress/sql/sequence.sql | 25 +++++-- 17 files changed, 312 insertions(+), 120 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 524180e011..162975746d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..cfbea6f0f8 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmax > PG_INT16_MAX) + || (seqform->seqtypid == INT4OID && seqform->seqmax > PG_INT32_MAX) + || (seqform->seqtypid == INT8OID && seqform->seqmax > PG_INT64_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmin < PG_INT16_MIN) + || (seqform->seqtypid == INT4OID && seqform->seqmin < PG_INT32_MIN) + || (seqform->seqtypid == INT8OID && seqform->seqmin < PG_INT64_MIN)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a8e35feccc..62c2bb6ff8 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c1f492b396..5ab96099ae 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 70719a36a7..250f53cffe 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index a2bdd3002b..768ba3b03f 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 5d7ab72944..ae7d834e20 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index a79330e780..a30a4933d1 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -143,16 +159,13 @@ CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; -- Test comments -- 2.11.0 --------------B52E26A5778158D3CF6AABE7 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------B52E26A5778158D3CF6AABE7-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v4] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-25 14:35 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-25 14:35 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 52 ++++++++++---- src/test/regress/expected/sequence_1.out | 52 ++++++++++---- src/test/regress/sql/sequence.sql | 25 +++++-- 17 files changed, 312 insertions(+), 120 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 524180e011..162975746d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..cfbea6f0f8 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmax > PG_INT16_MAX) + || (seqform->seqtypid == INT4OID && seqform->seqmax > PG_INT32_MAX) + || (seqform->seqtypid == INT8OID && seqform->seqmax > PG_INT64_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmin < PG_INT16_MIN) + || (seqform->seqtypid == INT4OID && seqform->seqmin < PG_INT32_MIN) + || (seqform->seqtypid == INT8OID && seqform->seqmin < PG_INT64_MIN)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a8e35feccc..62c2bb6ff8 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c1f492b396..5ab96099ae 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 70719a36a7..250f53cffe 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index a2bdd3002b..768ba3b03f 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 5d7ab72944..ae7d834e20 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index a79330e780..a30a4933d1 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -143,16 +159,13 @@ CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; -- Test comments -- 2.11.0 --------------B52E26A5778158D3CF6AABE7 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------B52E26A5778158D3CF6AABE7-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v4] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-25 14:35 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-25 14:35 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 52 ++++++++++---- src/test/regress/expected/sequence_1.out | 52 ++++++++++---- src/test/regress/sql/sequence.sql | 25 +++++-- 17 files changed, 312 insertions(+), 120 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 524180e011..162975746d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..cfbea6f0f8 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmax > PG_INT16_MAX) + || (seqform->seqtypid == INT4OID && seqform->seqmax > PG_INT32_MAX) + || (seqform->seqtypid == INT8OID && seqform->seqmax > PG_INT64_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmin < PG_INT16_MIN) + || (seqform->seqtypid == INT4OID && seqform->seqmin < PG_INT32_MIN) + || (seqform->seqtypid == INT8OID && seqform->seqmin < PG_INT64_MIN)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a8e35feccc..62c2bb6ff8 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c1f492b396..5ab96099ae 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 70719a36a7..250f53cffe 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index a2bdd3002b..768ba3b03f 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 5d7ab72944..ae7d834e20 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index a79330e780..a30a4933d1 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -143,16 +159,13 @@ CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; -- Test comments -- 2.11.0 --------------B52E26A5778158D3CF6AABE7 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------B52E26A5778158D3CF6AABE7-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v4] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-25 14:35 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-25 14:35 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 52 ++++++++++---- src/test/regress/expected/sequence_1.out | 52 ++++++++++---- src/test/regress/sql/sequence.sql | 25 +++++-- 17 files changed, 312 insertions(+), 120 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 524180e011..162975746d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..cfbea6f0f8 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmax > PG_INT16_MAX) + || (seqform->seqtypid == INT4OID && seqform->seqmax > PG_INT32_MAX) + || (seqform->seqtypid == INT8OID && seqform->seqmax > PG_INT64_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmin < PG_INT16_MIN) + || (seqform->seqtypid == INT4OID && seqform->seqmin < PG_INT32_MIN) + || (seqform->seqtypid == INT8OID && seqform->seqmin < PG_INT64_MIN)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a8e35feccc..62c2bb6ff8 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c1f492b396..5ab96099ae 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 70719a36a7..250f53cffe 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index a2bdd3002b..768ba3b03f 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 5d7ab72944..ae7d834e20 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index a79330e780..a30a4933d1 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -143,16 +159,13 @@ CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; -- Test comments -- 2.11.0 --------------B52E26A5778158D3CF6AABE7 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------B52E26A5778158D3CF6AABE7-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v4] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-25 14:35 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-25 14:35 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 52 ++++++++++---- src/test/regress/expected/sequence_1.out | 52 ++++++++++---- src/test/regress/sql/sequence.sql | 25 +++++-- 17 files changed, 312 insertions(+), 120 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 524180e011..162975746d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..cfbea6f0f8 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmax > PG_INT16_MAX) + || (seqform->seqtypid == INT4OID && seqform->seqmax > PG_INT32_MAX) + || (seqform->seqtypid == INT8OID && seqform->seqmax > PG_INT64_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmin < PG_INT16_MIN) + || (seqform->seqtypid == INT4OID && seqform->seqmin < PG_INT32_MIN) + || (seqform->seqtypid == INT8OID && seqform->seqmin < PG_INT64_MIN)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a8e35feccc..62c2bb6ff8 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c1f492b396..5ab96099ae 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 70719a36a7..250f53cffe 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index a2bdd3002b..768ba3b03f 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 5d7ab72944..ae7d834e20 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index a79330e780..a30a4933d1 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -143,16 +159,13 @@ CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; -- Test comments -- 2.11.0 --------------B52E26A5778158D3CF6AABE7 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------B52E26A5778158D3CF6AABE7-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v4] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-25 14:35 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-25 14:35 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 52 ++++++++++---- src/test/regress/expected/sequence_1.out | 52 ++++++++++---- src/test/regress/sql/sequence.sql | 25 +++++-- 17 files changed, 312 insertions(+), 120 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 524180e011..162975746d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..cfbea6f0f8 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmax > PG_INT16_MAX) + || (seqform->seqtypid == INT4OID && seqform->seqmax > PG_INT32_MAX) + || (seqform->seqtypid == INT8OID && seqform->seqmax > PG_INT64_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmin < PG_INT16_MIN) + || (seqform->seqtypid == INT4OID && seqform->seqmin < PG_INT32_MIN) + || (seqform->seqtypid == INT8OID && seqform->seqmin < PG_INT64_MIN)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a8e35feccc..62c2bb6ff8 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c1f492b396..5ab96099ae 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 70719a36a7..250f53cffe 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index a2bdd3002b..768ba3b03f 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 5d7ab72944..ae7d834e20 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index a79330e780..a30a4933d1 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -143,16 +159,13 @@ CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; -- Test comments -- 2.11.0 --------------B52E26A5778158D3CF6AABE7 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------B52E26A5778158D3CF6AABE7-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v4] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-25 14:35 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-25 14:35 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 52 ++++++++++---- src/test/regress/expected/sequence_1.out | 52 ++++++++++---- src/test/regress/sql/sequence.sql | 25 +++++-- 17 files changed, 312 insertions(+), 120 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 524180e011..162975746d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..cfbea6f0f8 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmax > PG_INT16_MAX) + || (seqform->seqtypid == INT4OID && seqform->seqmax > PG_INT32_MAX) + || (seqform->seqtypid == INT8OID && seqform->seqmax > PG_INT64_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmin < PG_INT16_MIN) + || (seqform->seqtypid == INT4OID && seqform->seqmin < PG_INT32_MIN) + || (seqform->seqtypid == INT8OID && seqform->seqmin < PG_INT64_MIN)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a8e35feccc..62c2bb6ff8 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c1f492b396..5ab96099ae 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 70719a36a7..250f53cffe 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index a2bdd3002b..768ba3b03f 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 5d7ab72944..ae7d834e20 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index a79330e780..a30a4933d1 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -143,16 +159,13 @@ CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; -- Test comments -- 2.11.0 --------------B52E26A5778158D3CF6AABE7 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------B52E26A5778158D3CF6AABE7-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v4] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-25 14:35 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-25 14:35 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 52 ++++++++++---- src/test/regress/expected/sequence_1.out | 52 ++++++++++---- src/test/regress/sql/sequence.sql | 25 +++++-- 17 files changed, 312 insertions(+), 120 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 524180e011..162975746d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..cfbea6f0f8 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmax > PG_INT16_MAX) + || (seqform->seqtypid == INT4OID && seqform->seqmax > PG_INT32_MAX) + || (seqform->seqtypid == INT8OID && seqform->seqmax > PG_INT64_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmin < PG_INT16_MIN) + || (seqform->seqtypid == INT4OID && seqform->seqmin < PG_INT32_MIN) + || (seqform->seqtypid == INT8OID && seqform->seqmin < PG_INT64_MIN)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a8e35feccc..62c2bb6ff8 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c1f492b396..5ab96099ae 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 70719a36a7..250f53cffe 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index a2bdd3002b..768ba3b03f 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 5d7ab72944..ae7d834e20 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index a79330e780..a30a4933d1 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -143,16 +159,13 @@ CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; -- Test comments -- 2.11.0 --------------B52E26A5778158D3CF6AABE7 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------B52E26A5778158D3CF6AABE7-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v4] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-25 14:35 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-25 14:35 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 52 ++++++++++---- src/test/regress/expected/sequence_1.out | 52 ++++++++++---- src/test/regress/sql/sequence.sql | 25 +++++-- 17 files changed, 312 insertions(+), 120 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 524180e011..162975746d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..cfbea6f0f8 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmax > PG_INT16_MAX) + || (seqform->seqtypid == INT4OID && seqform->seqmax > PG_INT32_MAX) + || (seqform->seqtypid == INT8OID && seqform->seqmax > PG_INT64_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmin < PG_INT16_MIN) + || (seqform->seqtypid == INT4OID && seqform->seqmin < PG_INT32_MIN) + || (seqform->seqtypid == INT8OID && seqform->seqmin < PG_INT64_MIN)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a8e35feccc..62c2bb6ff8 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c1f492b396..5ab96099ae 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 70719a36a7..250f53cffe 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index a2bdd3002b..768ba3b03f 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 5d7ab72944..ae7d834e20 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index a79330e780..a30a4933d1 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -143,16 +159,13 @@ CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; -- Test comments -- 2.11.0 --------------B52E26A5778158D3CF6AABE7 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------B52E26A5778158D3CF6AABE7-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v4] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-25 14:35 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-25 14:35 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 52 ++++++++++---- src/test/regress/expected/sequence_1.out | 52 ++++++++++---- src/test/regress/sql/sequence.sql | 25 +++++-- 17 files changed, 312 insertions(+), 120 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 524180e011..162975746d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..cfbea6f0f8 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmax > PG_INT16_MAX) + || (seqform->seqtypid == INT4OID && seqform->seqmax > PG_INT32_MAX) + || (seqform->seqtypid == INT8OID && seqform->seqmax > PG_INT64_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmin < PG_INT16_MIN) + || (seqform->seqtypid == INT4OID && seqform->seqmin < PG_INT32_MIN) + || (seqform->seqtypid == INT8OID && seqform->seqmin < PG_INT64_MIN)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a8e35feccc..62c2bb6ff8 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c1f492b396..5ab96099ae 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 70719a36a7..250f53cffe 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index a2bdd3002b..768ba3b03f 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 5d7ab72944..ae7d834e20 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index a79330e780..a30a4933d1 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -143,16 +159,13 @@ CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; -- Test comments -- 2.11.0 --------------B52E26A5778158D3CF6AABE7 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------B52E26A5778158D3CF6AABE7-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v4] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-25 14:35 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-25 14:35 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 52 ++++++++++---- src/test/regress/expected/sequence_1.out | 52 ++++++++++---- src/test/regress/sql/sequence.sql | 25 +++++-- 17 files changed, 312 insertions(+), 120 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 524180e011..162975746d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..cfbea6f0f8 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmax > PG_INT16_MAX) + || (seqform->seqtypid == INT4OID && seqform->seqmax > PG_INT32_MAX) + || (seqform->seqtypid == INT8OID && seqform->seqmax > PG_INT64_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmin < PG_INT16_MIN) + || (seqform->seqtypid == INT4OID && seqform->seqmin < PG_INT32_MIN) + || (seqform->seqtypid == INT8OID && seqform->seqmin < PG_INT64_MIN)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a8e35feccc..62c2bb6ff8 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c1f492b396..5ab96099ae 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 70719a36a7..250f53cffe 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index a2bdd3002b..768ba3b03f 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 5d7ab72944..ae7d834e20 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index a79330e780..a30a4933d1 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -143,16 +159,13 @@ CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; -- Test comments -- 2.11.0 --------------B52E26A5778158D3CF6AABE7 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------B52E26A5778158D3CF6AABE7-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v4] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-25 14:35 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-25 14:35 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 52 ++++++++++---- src/test/regress/expected/sequence_1.out | 52 ++++++++++---- src/test/regress/sql/sequence.sql | 25 +++++-- 17 files changed, 312 insertions(+), 120 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 524180e011..162975746d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..cfbea6f0f8 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmax > PG_INT16_MAX) + || (seqform->seqtypid == INT4OID && seqform->seqmax > PG_INT32_MAX) + || (seqform->seqtypid == INT8OID && seqform->seqmax > PG_INT64_MAX)) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is too large for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && seqform->seqmin < PG_INT16_MIN) + || (seqform->seqtypid == INT4OID && seqform->seqmin < PG_INT32_MIN) + || (seqform->seqtypid == INT8OID && seqform->seqmin < PG_INT64_MIN)) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is too large for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a8e35feccc..62c2bb6ff8 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c1f492b396..5ab96099ae 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index 70719a36a7..250f53cffe 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -226,6 +226,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index a2bdd3002b..768ba3b03f 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out index 5d7ab72944..ae7d834e20 100644 --- a/src/test/regress/expected/sequence_1.out +++ b/src/test/regress/expected/sequence_1.out @@ -1,3 +1,22 @@ +-- +-- CREATE SEQUENCE +-- +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is too large for sequence data type smallint +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is too large for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is too large for sequence data type smallint --- --- test creation of SERIAL column --- @@ -302,37 +321,42 @@ SELECT nextval('sequence_test2'); CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | increment | cycle_option ------------------+-----------------+--------------------+-----------+-------------------+-------------------------+---------------+-------------+---------------+---------------------+-----------+-------------- regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test4 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(7 rows) + regression | public | serialtest_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO +(11 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value ------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test4 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(7 rows) + public | serialtest_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 +(11 rows) -- Test comments COMMENT ON SEQUENCE asdf IS 'won''t work'; diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index a79330e780..a30a4933d1 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -1,3 +1,19 @@ +-- +-- CREATE SEQUENCE +-- + +CREATE SEQUENCE sequence_test4 AS integer; +CREATE SEQUENCE sequence_test5 AS smallint; +CREATE SEQUENCE sequence_test6 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test4 AS smallint; -- fails +ALTER SEQUENCE sequence_test4 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- @@ -143,16 +159,13 @@ CREATE SEQUENCE sequence_test3; -- not read from, to test is_called -- Information schema -SELECT * FROM information_schema.sequences WHERE sequence_name IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +SELECT * FROM information_schema.sequences + WHERE sequence_name ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequence_name ASC; SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences -WHERE sequencename IN - ('sequence_test2', 'sequence_test3', 'serialtest2_f2_seq', 'serialtest2_f3_seq', - 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq') +WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ORDER BY sequencename ASC; -- Test comments -- 2.11.0 --------------B52E26A5778158D3CF6AABE7 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------B52E26A5778158D3CF6AABE7-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v5] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-28 04:52 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-28 04:52 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 49 +++++++++---- src/test/regress/sql/sequence.sql | 13 ++++ 16 files changed, 266 insertions(+), 99 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 086fafc694..f57937a17a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..624c62b427 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmax < PG_INT64_MIN || seqform->seqmax > PG_INT64_MAX))) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is out of range for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmin < PG_INT64_MIN || seqform->seqmin > PG_INT64_MAX))) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is out of range for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a4edea08a3..09ac4572ab 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 31c828a3f2..874f1a9dcf 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index c8e8d4a94c..bb2a65782a 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -305,6 +305,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index d062e91d26..f339489151 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -28,6 +28,23 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column ERROR: column "b" of relation "sequence_test_table" does not exist DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is out of range for sequence data type smallint +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is out of range for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is out of range for sequence data type smallint --- --- test creation of SERIAL column --- @@ -445,13 +462,16 @@ SELECT * FROM information_schema.sequences regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | sequence_test4 | bigint | 64 | 2 | 0 | -1 | -9223372036854775808 | -1 | -1 | NO - regression | public | serialtest1_f2_foo | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test7 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest1_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(9 rows) +(12 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences @@ -462,18 +482,21 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | public | sequence_test4 | -1 | -9223372036854775808 | -1 | -1 | f | 1 | -1 - public | serialtest1_f2_foo | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 3 - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test7 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest1_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(9 rows) +(12 rows) SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); - start_value | minimum_value | maximum_value | increment | cycle_option | cache_size --------------+----------------------+---------------+-----------+--------------+------------ - -1 | -9223372036854775808 | -1 | -1 | f | 1 + start_value | minimum_value | maximum_value | increment | cycle_option | cache_size | data_type +-------------+----------------------+---------------+-----------+--------------+------------+----------- + -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 (1 row) -- Test comments diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 4b9824c3cc..0fbd255967 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -19,6 +19,19 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- -- 2.11.0 --------------9CA65CF546BB80FC2F613C34 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9CA65CF546BB80FC2F613C34-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v5] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-28 04:52 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-28 04:52 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 49 +++++++++---- src/test/regress/sql/sequence.sql | 13 ++++ 16 files changed, 266 insertions(+), 99 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 086fafc694..f57937a17a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..624c62b427 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmax < PG_INT64_MIN || seqform->seqmax > PG_INT64_MAX))) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is out of range for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmin < PG_INT64_MIN || seqform->seqmin > PG_INT64_MAX))) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is out of range for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a4edea08a3..09ac4572ab 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 31c828a3f2..874f1a9dcf 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index c8e8d4a94c..bb2a65782a 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -305,6 +305,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index d062e91d26..f339489151 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -28,6 +28,23 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column ERROR: column "b" of relation "sequence_test_table" does not exist DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is out of range for sequence data type smallint +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is out of range for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is out of range for sequence data type smallint --- --- test creation of SERIAL column --- @@ -445,13 +462,16 @@ SELECT * FROM information_schema.sequences regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | sequence_test4 | bigint | 64 | 2 | 0 | -1 | -9223372036854775808 | -1 | -1 | NO - regression | public | serialtest1_f2_foo | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test7 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest1_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(9 rows) +(12 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences @@ -462,18 +482,21 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | public | sequence_test4 | -1 | -9223372036854775808 | -1 | -1 | f | 1 | -1 - public | serialtest1_f2_foo | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 3 - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test7 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest1_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(9 rows) +(12 rows) SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); - start_value | minimum_value | maximum_value | increment | cycle_option | cache_size --------------+----------------------+---------------+-----------+--------------+------------ - -1 | -9223372036854775808 | -1 | -1 | f | 1 + start_value | minimum_value | maximum_value | increment | cycle_option | cache_size | data_type +-------------+----------------------+---------------+-----------+--------------+------------+----------- + -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 (1 row) -- Test comments diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 4b9824c3cc..0fbd255967 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -19,6 +19,19 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- -- 2.11.0 --------------9CA65CF546BB80FC2F613C34 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9CA65CF546BB80FC2F613C34-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v5] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-28 04:52 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-28 04:52 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 49 +++++++++---- src/test/regress/sql/sequence.sql | 13 ++++ 16 files changed, 266 insertions(+), 99 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 086fafc694..f57937a17a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..624c62b427 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmax < PG_INT64_MIN || seqform->seqmax > PG_INT64_MAX))) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is out of range for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmin < PG_INT64_MIN || seqform->seqmin > PG_INT64_MAX))) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is out of range for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a4edea08a3..09ac4572ab 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 31c828a3f2..874f1a9dcf 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index c8e8d4a94c..bb2a65782a 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -305,6 +305,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index d062e91d26..f339489151 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -28,6 +28,23 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column ERROR: column "b" of relation "sequence_test_table" does not exist DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is out of range for sequence data type smallint +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is out of range for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is out of range for sequence data type smallint --- --- test creation of SERIAL column --- @@ -445,13 +462,16 @@ SELECT * FROM information_schema.sequences regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | sequence_test4 | bigint | 64 | 2 | 0 | -1 | -9223372036854775808 | -1 | -1 | NO - regression | public | serialtest1_f2_foo | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test7 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest1_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(9 rows) +(12 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences @@ -462,18 +482,21 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | public | sequence_test4 | -1 | -9223372036854775808 | -1 | -1 | f | 1 | -1 - public | serialtest1_f2_foo | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 3 - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test7 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest1_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(9 rows) +(12 rows) SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); - start_value | minimum_value | maximum_value | increment | cycle_option | cache_size --------------+----------------------+---------------+-----------+--------------+------------ - -1 | -9223372036854775808 | -1 | -1 | f | 1 + start_value | minimum_value | maximum_value | increment | cycle_option | cache_size | data_type +-------------+----------------------+---------------+-----------+--------------+------------+----------- + -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 (1 row) -- Test comments diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 4b9824c3cc..0fbd255967 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -19,6 +19,19 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- -- 2.11.0 --------------9CA65CF546BB80FC2F613C34 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9CA65CF546BB80FC2F613C34-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v5] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-28 04:52 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-28 04:52 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 49 +++++++++---- src/test/regress/sql/sequence.sql | 13 ++++ 16 files changed, 266 insertions(+), 99 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 086fafc694..f57937a17a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..624c62b427 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmax < PG_INT64_MIN || seqform->seqmax > PG_INT64_MAX))) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is out of range for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmin < PG_INT64_MIN || seqform->seqmin > PG_INT64_MAX))) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is out of range for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a4edea08a3..09ac4572ab 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 31c828a3f2..874f1a9dcf 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index c8e8d4a94c..bb2a65782a 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -305,6 +305,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index d062e91d26..f339489151 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -28,6 +28,23 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column ERROR: column "b" of relation "sequence_test_table" does not exist DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is out of range for sequence data type smallint +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is out of range for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is out of range for sequence data type smallint --- --- test creation of SERIAL column --- @@ -445,13 +462,16 @@ SELECT * FROM information_schema.sequences regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | sequence_test4 | bigint | 64 | 2 | 0 | -1 | -9223372036854775808 | -1 | -1 | NO - regression | public | serialtest1_f2_foo | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test7 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest1_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(9 rows) +(12 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences @@ -462,18 +482,21 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | public | sequence_test4 | -1 | -9223372036854775808 | -1 | -1 | f | 1 | -1 - public | serialtest1_f2_foo | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 3 - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test7 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest1_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(9 rows) +(12 rows) SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); - start_value | minimum_value | maximum_value | increment | cycle_option | cache_size --------------+----------------------+---------------+-----------+--------------+------------ - -1 | -9223372036854775808 | -1 | -1 | f | 1 + start_value | minimum_value | maximum_value | increment | cycle_option | cache_size | data_type +-------------+----------------------+---------------+-----------+--------------+------------+----------- + -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 (1 row) -- Test comments diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 4b9824c3cc..0fbd255967 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -19,6 +19,19 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- -- 2.11.0 --------------9CA65CF546BB80FC2F613C34 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9CA65CF546BB80FC2F613C34-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v5] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-28 04:52 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-28 04:52 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 49 +++++++++---- src/test/regress/sql/sequence.sql | 13 ++++ 16 files changed, 266 insertions(+), 99 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 086fafc694..f57937a17a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..624c62b427 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmax < PG_INT64_MIN || seqform->seqmax > PG_INT64_MAX))) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is out of range for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmin < PG_INT64_MIN || seqform->seqmin > PG_INT64_MAX))) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is out of range for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a4edea08a3..09ac4572ab 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 31c828a3f2..874f1a9dcf 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index c8e8d4a94c..bb2a65782a 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -305,6 +305,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index d062e91d26..f339489151 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -28,6 +28,23 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column ERROR: column "b" of relation "sequence_test_table" does not exist DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is out of range for sequence data type smallint +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is out of range for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is out of range for sequence data type smallint --- --- test creation of SERIAL column --- @@ -445,13 +462,16 @@ SELECT * FROM information_schema.sequences regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | sequence_test4 | bigint | 64 | 2 | 0 | -1 | -9223372036854775808 | -1 | -1 | NO - regression | public | serialtest1_f2_foo | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test7 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest1_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(9 rows) +(12 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences @@ -462,18 +482,21 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | public | sequence_test4 | -1 | -9223372036854775808 | -1 | -1 | f | 1 | -1 - public | serialtest1_f2_foo | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 3 - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test7 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest1_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(9 rows) +(12 rows) SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); - start_value | minimum_value | maximum_value | increment | cycle_option | cache_size --------------+----------------------+---------------+-----------+--------------+------------ - -1 | -9223372036854775808 | -1 | -1 | f | 1 + start_value | minimum_value | maximum_value | increment | cycle_option | cache_size | data_type +-------------+----------------------+---------------+-----------+--------------+------------+----------- + -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 (1 row) -- Test comments diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 4b9824c3cc..0fbd255967 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -19,6 +19,19 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- -- 2.11.0 --------------9CA65CF546BB80FC2F613C34 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9CA65CF546BB80FC2F613C34-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v5] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-28 04:52 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-28 04:52 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 49 +++++++++---- src/test/regress/sql/sequence.sql | 13 ++++ 16 files changed, 266 insertions(+), 99 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 086fafc694..f57937a17a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..624c62b427 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmax < PG_INT64_MIN || seqform->seqmax > PG_INT64_MAX))) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is out of range for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmin < PG_INT64_MIN || seqform->seqmin > PG_INT64_MAX))) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is out of range for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a4edea08a3..09ac4572ab 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 31c828a3f2..874f1a9dcf 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index c8e8d4a94c..bb2a65782a 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -305,6 +305,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index d062e91d26..f339489151 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -28,6 +28,23 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column ERROR: column "b" of relation "sequence_test_table" does not exist DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is out of range for sequence data type smallint +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is out of range for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is out of range for sequence data type smallint --- --- test creation of SERIAL column --- @@ -445,13 +462,16 @@ SELECT * FROM information_schema.sequences regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | sequence_test4 | bigint | 64 | 2 | 0 | -1 | -9223372036854775808 | -1 | -1 | NO - regression | public | serialtest1_f2_foo | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test7 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest1_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(9 rows) +(12 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences @@ -462,18 +482,21 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | public | sequence_test4 | -1 | -9223372036854775808 | -1 | -1 | f | 1 | -1 - public | serialtest1_f2_foo | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 3 - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test7 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest1_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(9 rows) +(12 rows) SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); - start_value | minimum_value | maximum_value | increment | cycle_option | cache_size --------------+----------------------+---------------+-----------+--------------+------------ - -1 | -9223372036854775808 | -1 | -1 | f | 1 + start_value | minimum_value | maximum_value | increment | cycle_option | cache_size | data_type +-------------+----------------------+---------------+-----------+--------------+------------+----------- + -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 (1 row) -- Test comments diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 4b9824c3cc..0fbd255967 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -19,6 +19,19 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- -- 2.11.0 --------------9CA65CF546BB80FC2F613C34 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9CA65CF546BB80FC2F613C34-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v5] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-28 04:52 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-28 04:52 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 49 +++++++++---- src/test/regress/sql/sequence.sql | 13 ++++ 16 files changed, 266 insertions(+), 99 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 086fafc694..f57937a17a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..624c62b427 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmax < PG_INT64_MIN || seqform->seqmax > PG_INT64_MAX))) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is out of range for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmin < PG_INT64_MIN || seqform->seqmin > PG_INT64_MAX))) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is out of range for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a4edea08a3..09ac4572ab 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 31c828a3f2..874f1a9dcf 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index c8e8d4a94c..bb2a65782a 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -305,6 +305,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index d062e91d26..f339489151 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -28,6 +28,23 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column ERROR: column "b" of relation "sequence_test_table" does not exist DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is out of range for sequence data type smallint +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is out of range for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is out of range for sequence data type smallint --- --- test creation of SERIAL column --- @@ -445,13 +462,16 @@ SELECT * FROM information_schema.sequences regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | sequence_test4 | bigint | 64 | 2 | 0 | -1 | -9223372036854775808 | -1 | -1 | NO - regression | public | serialtest1_f2_foo | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test7 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest1_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(9 rows) +(12 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences @@ -462,18 +482,21 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | public | sequence_test4 | -1 | -9223372036854775808 | -1 | -1 | f | 1 | -1 - public | serialtest1_f2_foo | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 3 - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test7 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest1_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(9 rows) +(12 rows) SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); - start_value | minimum_value | maximum_value | increment | cycle_option | cache_size --------------+----------------------+---------------+-----------+--------------+------------ - -1 | -9223372036854775808 | -1 | -1 | f | 1 + start_value | minimum_value | maximum_value | increment | cycle_option | cache_size | data_type +-------------+----------------------+---------------+-----------+--------------+------------+----------- + -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 (1 row) -- Test comments diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 4b9824c3cc..0fbd255967 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -19,6 +19,19 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- -- 2.11.0 --------------9CA65CF546BB80FC2F613C34 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9CA65CF546BB80FC2F613C34-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v5] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-28 04:52 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-28 04:52 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 49 +++++++++---- src/test/regress/sql/sequence.sql | 13 ++++ 16 files changed, 266 insertions(+), 99 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 086fafc694..f57937a17a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..624c62b427 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmax < PG_INT64_MIN || seqform->seqmax > PG_INT64_MAX))) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is out of range for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmin < PG_INT64_MIN || seqform->seqmin > PG_INT64_MAX))) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is out of range for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a4edea08a3..09ac4572ab 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 31c828a3f2..874f1a9dcf 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index c8e8d4a94c..bb2a65782a 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -305,6 +305,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index d062e91d26..f339489151 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -28,6 +28,23 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column ERROR: column "b" of relation "sequence_test_table" does not exist DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is out of range for sequence data type smallint +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is out of range for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is out of range for sequence data type smallint --- --- test creation of SERIAL column --- @@ -445,13 +462,16 @@ SELECT * FROM information_schema.sequences regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | sequence_test4 | bigint | 64 | 2 | 0 | -1 | -9223372036854775808 | -1 | -1 | NO - regression | public | serialtest1_f2_foo | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test7 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest1_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(9 rows) +(12 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences @@ -462,18 +482,21 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | public | sequence_test4 | -1 | -9223372036854775808 | -1 | -1 | f | 1 | -1 - public | serialtest1_f2_foo | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 3 - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test7 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest1_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(9 rows) +(12 rows) SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); - start_value | minimum_value | maximum_value | increment | cycle_option | cache_size --------------+----------------------+---------------+-----------+--------------+------------ - -1 | -9223372036854775808 | -1 | -1 | f | 1 + start_value | minimum_value | maximum_value | increment | cycle_option | cache_size | data_type +-------------+----------------------+---------------+-----------+--------------+------------+----------- + -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 (1 row) -- Test comments diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 4b9824c3cc..0fbd255967 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -19,6 +19,19 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- -- 2.11.0 --------------9CA65CF546BB80FC2F613C34 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9CA65CF546BB80FC2F613C34-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v5] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-28 04:52 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-28 04:52 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 49 +++++++++---- src/test/regress/sql/sequence.sql | 13 ++++ 16 files changed, 266 insertions(+), 99 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 086fafc694..f57937a17a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..624c62b427 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmax < PG_INT64_MIN || seqform->seqmax > PG_INT64_MAX))) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is out of range for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmin < PG_INT64_MIN || seqform->seqmin > PG_INT64_MAX))) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is out of range for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a4edea08a3..09ac4572ab 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 31c828a3f2..874f1a9dcf 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index c8e8d4a94c..bb2a65782a 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -305,6 +305,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index d062e91d26..f339489151 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -28,6 +28,23 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column ERROR: column "b" of relation "sequence_test_table" does not exist DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is out of range for sequence data type smallint +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is out of range for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is out of range for sequence data type smallint --- --- test creation of SERIAL column --- @@ -445,13 +462,16 @@ SELECT * FROM information_schema.sequences regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | sequence_test4 | bigint | 64 | 2 | 0 | -1 | -9223372036854775808 | -1 | -1 | NO - regression | public | serialtest1_f2_foo | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test7 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest1_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(9 rows) +(12 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences @@ -462,18 +482,21 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | public | sequence_test4 | -1 | -9223372036854775808 | -1 | -1 | f | 1 | -1 - public | serialtest1_f2_foo | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 3 - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test7 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest1_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(9 rows) +(12 rows) SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); - start_value | minimum_value | maximum_value | increment | cycle_option | cache_size --------------+----------------------+---------------+-----------+--------------+------------ - -1 | -9223372036854775808 | -1 | -1 | f | 1 + start_value | minimum_value | maximum_value | increment | cycle_option | cache_size | data_type +-------------+----------------------+---------------+-----------+--------------+------------+----------- + -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 (1 row) -- Test comments diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 4b9824c3cc..0fbd255967 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -19,6 +19,19 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- -- 2.11.0 --------------9CA65CF546BB80FC2F613C34 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9CA65CF546BB80FC2F613C34-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v5] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-28 04:52 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-28 04:52 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 49 +++++++++---- src/test/regress/sql/sequence.sql | 13 ++++ 16 files changed, 266 insertions(+), 99 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 086fafc694..f57937a17a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..624c62b427 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmax < PG_INT64_MIN || seqform->seqmax > PG_INT64_MAX))) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is out of range for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmin < PG_INT64_MIN || seqform->seqmin > PG_INT64_MAX))) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is out of range for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a4edea08a3..09ac4572ab 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 31c828a3f2..874f1a9dcf 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index c8e8d4a94c..bb2a65782a 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -305,6 +305,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index d062e91d26..f339489151 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -28,6 +28,23 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column ERROR: column "b" of relation "sequence_test_table" does not exist DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is out of range for sequence data type smallint +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is out of range for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is out of range for sequence data type smallint --- --- test creation of SERIAL column --- @@ -445,13 +462,16 @@ SELECT * FROM information_schema.sequences regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | sequence_test4 | bigint | 64 | 2 | 0 | -1 | -9223372036854775808 | -1 | -1 | NO - regression | public | serialtest1_f2_foo | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test7 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest1_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(9 rows) +(12 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences @@ -462,18 +482,21 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | public | sequence_test4 | -1 | -9223372036854775808 | -1 | -1 | f | 1 | -1 - public | serialtest1_f2_foo | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 3 - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test7 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest1_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(9 rows) +(12 rows) SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); - start_value | minimum_value | maximum_value | increment | cycle_option | cache_size --------------+----------------------+---------------+-----------+--------------+------------ - -1 | -9223372036854775808 | -1 | -1 | f | 1 + start_value | minimum_value | maximum_value | increment | cycle_option | cache_size | data_type +-------------+----------------------+---------------+-----------+--------------+------------+----------- + -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 (1 row) -- Test comments diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 4b9824c3cc..0fbd255967 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -19,6 +19,19 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- -- 2.11.0 --------------9CA65CF546BB80FC2F613C34 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9CA65CF546BB80FC2F613C34-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v5] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-28 04:52 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-28 04:52 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 49 +++++++++---- src/test/regress/sql/sequence.sql | 13 ++++ 16 files changed, 266 insertions(+), 99 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 086fafc694..f57937a17a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..624c62b427 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmax < PG_INT64_MIN || seqform->seqmax > PG_INT64_MAX))) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is out of range for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmin < PG_INT64_MIN || seqform->seqmin > PG_INT64_MAX))) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is out of range for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a4edea08a3..09ac4572ab 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 31c828a3f2..874f1a9dcf 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index c8e8d4a94c..bb2a65782a 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -305,6 +305,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index d062e91d26..f339489151 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -28,6 +28,23 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column ERROR: column "b" of relation "sequence_test_table" does not exist DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is out of range for sequence data type smallint +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is out of range for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is out of range for sequence data type smallint --- --- test creation of SERIAL column --- @@ -445,13 +462,16 @@ SELECT * FROM information_schema.sequences regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | sequence_test4 | bigint | 64 | 2 | 0 | -1 | -9223372036854775808 | -1 | -1 | NO - regression | public | serialtest1_f2_foo | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test7 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest1_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(9 rows) +(12 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences @@ -462,18 +482,21 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | public | sequence_test4 | -1 | -9223372036854775808 | -1 | -1 | f | 1 | -1 - public | serialtest1_f2_foo | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 3 - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test7 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest1_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(9 rows) +(12 rows) SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); - start_value | minimum_value | maximum_value | increment | cycle_option | cache_size --------------+----------------------+---------------+-----------+--------------+------------ - -1 | -9223372036854775808 | -1 | -1 | f | 1 + start_value | minimum_value | maximum_value | increment | cycle_option | cache_size | data_type +-------------+----------------------+---------------+-----------+--------------+------------+----------- + -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 (1 row) -- Test comments diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 4b9824c3cc..0fbd255967 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -19,6 +19,19 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- -- 2.11.0 --------------9CA65CF546BB80FC2F613C34 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9CA65CF546BB80FC2F613C34-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v5] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-28 04:52 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-28 04:52 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 49 +++++++++---- src/test/regress/sql/sequence.sql | 13 ++++ 16 files changed, 266 insertions(+), 99 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 086fafc694..f57937a17a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..624c62b427 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmax < PG_INT64_MIN || seqform->seqmax > PG_INT64_MAX))) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is out of range for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmin < PG_INT64_MIN || seqform->seqmin > PG_INT64_MAX))) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is out of range for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a4edea08a3..09ac4572ab 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 31c828a3f2..874f1a9dcf 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index c8e8d4a94c..bb2a65782a 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -305,6 +305,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index d062e91d26..f339489151 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -28,6 +28,23 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column ERROR: column "b" of relation "sequence_test_table" does not exist DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is out of range for sequence data type smallint +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is out of range for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is out of range for sequence data type smallint --- --- test creation of SERIAL column --- @@ -445,13 +462,16 @@ SELECT * FROM information_schema.sequences regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | sequence_test4 | bigint | 64 | 2 | 0 | -1 | -9223372036854775808 | -1 | -1 | NO - regression | public | serialtest1_f2_foo | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test7 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest1_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(9 rows) +(12 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences @@ -462,18 +482,21 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | public | sequence_test4 | -1 | -9223372036854775808 | -1 | -1 | f | 1 | -1 - public | serialtest1_f2_foo | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 3 - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test7 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest1_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(9 rows) +(12 rows) SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); - start_value | minimum_value | maximum_value | increment | cycle_option | cache_size --------------+----------------------+---------------+-----------+--------------+------------ - -1 | -9223372036854775808 | -1 | -1 | f | 1 + start_value | minimum_value | maximum_value | increment | cycle_option | cache_size | data_type +-------------+----------------------+---------------+-----------+--------------+------------+----------- + -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 (1 row) -- Test comments diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 4b9824c3cc..0fbd255967 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -19,6 +19,19 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- -- 2.11.0 --------------9CA65CF546BB80FC2F613C34 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9CA65CF546BB80FC2F613C34-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v5] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-28 04:52 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-28 04:52 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 49 +++++++++---- src/test/regress/sql/sequence.sql | 13 ++++ 16 files changed, 266 insertions(+), 99 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 086fafc694..f57937a17a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..624c62b427 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmax < PG_INT64_MIN || seqform->seqmax > PG_INT64_MAX))) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is out of range for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmin < PG_INT64_MIN || seqform->seqmin > PG_INT64_MAX))) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is out of range for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a4edea08a3..09ac4572ab 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 31c828a3f2..874f1a9dcf 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index c8e8d4a94c..bb2a65782a 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -305,6 +305,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index d062e91d26..f339489151 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -28,6 +28,23 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column ERROR: column "b" of relation "sequence_test_table" does not exist DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is out of range for sequence data type smallint +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is out of range for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is out of range for sequence data type smallint --- --- test creation of SERIAL column --- @@ -445,13 +462,16 @@ SELECT * FROM information_schema.sequences regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | sequence_test4 | bigint | 64 | 2 | 0 | -1 | -9223372036854775808 | -1 | -1 | NO - regression | public | serialtest1_f2_foo | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test7 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest1_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(9 rows) +(12 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences @@ -462,18 +482,21 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | public | sequence_test4 | -1 | -9223372036854775808 | -1 | -1 | f | 1 | -1 - public | serialtest1_f2_foo | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 3 - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test7 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest1_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(9 rows) +(12 rows) SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); - start_value | minimum_value | maximum_value | increment | cycle_option | cache_size --------------+----------------------+---------------+-----------+--------------+------------ - -1 | -9223372036854775808 | -1 | -1 | f | 1 + start_value | minimum_value | maximum_value | increment | cycle_option | cache_size | data_type +-------------+----------------------+---------------+-----------+--------------+------------+----------- + -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 (1 row) -- Test comments diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 4b9824c3cc..0fbd255967 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -19,6 +19,19 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- -- 2.11.0 --------------9CA65CF546BB80FC2F613C34 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9CA65CF546BB80FC2F613C34-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v5] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-28 04:52 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-28 04:52 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 49 +++++++++---- src/test/regress/sql/sequence.sql | 13 ++++ 16 files changed, 266 insertions(+), 99 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 086fafc694..f57937a17a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..624c62b427 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmax < PG_INT64_MIN || seqform->seqmax > PG_INT64_MAX))) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is out of range for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmin < PG_INT64_MIN || seqform->seqmin > PG_INT64_MAX))) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is out of range for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a4edea08a3..09ac4572ab 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 31c828a3f2..874f1a9dcf 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index c8e8d4a94c..bb2a65782a 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -305,6 +305,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index d062e91d26..f339489151 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -28,6 +28,23 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column ERROR: column "b" of relation "sequence_test_table" does not exist DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is out of range for sequence data type smallint +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is out of range for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is out of range for sequence data type smallint --- --- test creation of SERIAL column --- @@ -445,13 +462,16 @@ SELECT * FROM information_schema.sequences regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | sequence_test4 | bigint | 64 | 2 | 0 | -1 | -9223372036854775808 | -1 | -1 | NO - regression | public | serialtest1_f2_foo | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test7 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest1_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(9 rows) +(12 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences @@ -462,18 +482,21 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | public | sequence_test4 | -1 | -9223372036854775808 | -1 | -1 | f | 1 | -1 - public | serialtest1_f2_foo | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 3 - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test7 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest1_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(9 rows) +(12 rows) SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); - start_value | minimum_value | maximum_value | increment | cycle_option | cache_size --------------+----------------------+---------------+-----------+--------------+------------ - -1 | -9223372036854775808 | -1 | -1 | f | 1 + start_value | minimum_value | maximum_value | increment | cycle_option | cache_size | data_type +-------------+----------------------+---------------+-----------+--------------+------------+----------- + -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 (1 row) -- Test comments diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 4b9824c3cc..0fbd255967 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -19,6 +19,19 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- -- 2.11.0 --------------9CA65CF546BB80FC2F613C34 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9CA65CF546BB80FC2F613C34-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v5] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-28 04:52 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-28 04:52 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 49 +++++++++---- src/test/regress/sql/sequence.sql | 13 ++++ 16 files changed, 266 insertions(+), 99 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 086fafc694..f57937a17a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..624c62b427 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmax < PG_INT64_MIN || seqform->seqmax > PG_INT64_MAX))) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is out of range for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmin < PG_INT64_MIN || seqform->seqmin > PG_INT64_MAX))) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is out of range for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a4edea08a3..09ac4572ab 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 31c828a3f2..874f1a9dcf 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index c8e8d4a94c..bb2a65782a 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -305,6 +305,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index d062e91d26..f339489151 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -28,6 +28,23 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column ERROR: column "b" of relation "sequence_test_table" does not exist DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is out of range for sequence data type smallint +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is out of range for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is out of range for sequence data type smallint --- --- test creation of SERIAL column --- @@ -445,13 +462,16 @@ SELECT * FROM information_schema.sequences regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | sequence_test4 | bigint | 64 | 2 | 0 | -1 | -9223372036854775808 | -1 | -1 | NO - regression | public | serialtest1_f2_foo | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test7 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest1_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(9 rows) +(12 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences @@ -462,18 +482,21 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | public | sequence_test4 | -1 | -9223372036854775808 | -1 | -1 | f | 1 | -1 - public | serialtest1_f2_foo | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 3 - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test7 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest1_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(9 rows) +(12 rows) SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); - start_value | minimum_value | maximum_value | increment | cycle_option | cache_size --------------+----------------------+---------------+-----------+--------------+------------ - -1 | -9223372036854775808 | -1 | -1 | f | 1 + start_value | minimum_value | maximum_value | increment | cycle_option | cache_size | data_type +-------------+----------------------+---------------+-----------+--------------+------------+----------- + -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 (1 row) -- Test comments diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 4b9824c3cc..0fbd255967 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -19,6 +19,19 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- -- 2.11.0 --------------9CA65CF546BB80FC2F613C34 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9CA65CF546BB80FC2F613C34-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v5] Add CREATE SEQUENCE AS <data type> clause @ 2017-01-28 04:52 Peter Eisentraut <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Peter Eisentraut @ 2017-01-28 04:52 UTC (permalink / raw) This stores a data type, required to be an integer type, with the sequence. The sequences min and max values default to the range supported by the type, and they cannot be set to values exceeding that range. The internal implementation of the sequence is not affected. Change the serial types to create sequences of the appropriate type. This makes sure that the min and max values of the sequence for a serial column match the range of values supported by the table column. So the sequence can no longer overflow the table column. This also makes monitoring for sequence exhaustion/wraparound easier, which currently requires various contortions to cross-reference the sequences with the table columns they are used with. This commit also effectively reverts the pg_sequence column reordering in f3b421da5f4addc95812b9db05a24972b8fd9739, because the new seqtypid column allows us to fill the hole in the struct and create a more natural overall column ordering. --- doc/src/sgml/catalogs.sgml | 14 +++- doc/src/sgml/information_schema.sgml | 4 +- doc/src/sgml/ref/alter_sequence.sgml | 30 ++++++-- doc/src/sgml/ref/create_sequence.sgml | 36 ++++++---- src/backend/catalog/information_schema.sql | 4 +- src/backend/commands/sequence.c | 89 +++++++++++++++++++++--- src/backend/parser/gram.y | 6 +- src/backend/parser/parse_utilcmd.c | 2 +- src/bin/pg_dump/pg_dump.c | 103 +++++++++++++++------------- src/bin/pg_dump/t/002_pg_dump.pl | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 2 +- src/include/catalog/pg_sequence.h | 8 ++- src/test/modules/test_pg_dump/t/001_base.pl | 1 + src/test/regress/expected/sequence.out | 49 +++++++++---- src/test/regress/sql/sequence.sql | 13 ++++ 16 files changed, 266 insertions(+), 99 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 086fafc694..f57937a17a 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5774,10 +5774,11 @@ <title><structname>pg_sequence</> Columns</title> </row> <row> - <entry><structfield>seqcycle</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>seqtypid</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry></entry> - <entry>Whether the sequence cycles</entry> + <entry>Data type of the sequence</entry> </row> <row> @@ -5814,6 +5815,13 @@ <title><structname>pg_sequence</> Columns</title> <entry></entry> <entry>Cache size of the sequence</entry> </row> + + <row> + <entry><structfield>seqcycle</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Whether the sequence cycles</entry> + </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index c43e325d06..a3a19ce8ce 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -4653,9 +4653,7 @@ <title><literal>sequences</literal> Columns</title> <entry><literal>data_type</literal></entry> <entry><type>character_data</type></entry> <entry> - The data type of the sequence. In - <productname>PostgreSQL</productname>, this is currently always - <literal>bigint</literal>. + The data type of the sequence. </entry> </row> diff --git a/doc/src/sgml/ref/alter_sequence.sgml b/doc/src/sgml/ref/alter_sequence.sgml index 3b52e875e3..252a668189 100644 --- a/doc/src/sgml/ref/alter_sequence.sgml +++ b/doc/src/sgml/ref/alter_sequence.sgml @@ -23,7 +23,9 @@ <refsynopsisdiv> <synopsis> -ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] ] @@ -81,6 +83,26 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + changes the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. + </para> + + <para> + Note that changing the data type does not automatically change the + minimum and maximum values. You can use the clauses <literal>NO + MINVALUE</literal> and <literal>NO MAXVALUE</literal> to adjust the + minimum and maximum values to the range of the new data type. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -102,7 +124,7 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If <literal>NO MINVALUE</literal> is specified, the defaults of 1 and - -2<superscript>63</> for ascending and descending sequences, + the minimum value of the data type for ascending and descending sequences, respectively, will be used. If neither option is specified, the current minimum value will be maintained. </para> @@ -118,7 +140,7 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If <literal>NO MAXVALUE</literal> is specified, the defaults of - 2<superscript>63</>-1 and -1 for ascending and descending + the maximum value of the data type and -1 for ascending and descending sequences, respectively, will be used. If neither option is specified, the current maximum value will be maintained. </para> @@ -300,7 +322,7 @@ <title>Compatibility</title> <para> <command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym> - standard, except for the <literal>START WITH</>, + standard, except for the <literal>AS</literal>, <literal>START WITH</>, <literal>OWNED BY</>, <literal>OWNER TO</>, <literal>RENAME TO</>, and <literal>SET SCHEMA</literal> clauses, which are <productname>PostgreSQL</productname> extensions. diff --git a/doc/src/sgml/ref/create_sequence.sgml b/doc/src/sgml/ref/create_sequence.sgml index 86ff018c4b..f1448e7ab3 100644 --- a/doc/src/sgml/ref/create_sequence.sgml +++ b/doc/src/sgml/ref/create_sequence.sgml @@ -21,7 +21,9 @@ <refsynopsisdiv> <synopsis> -CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] +CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> + [ AS <replaceable class="parameter">data_type</replaceable> ] + [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ] [ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ] [ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ] [ OWNED BY { <replaceable class="parameter">table_name</replaceable>.<replaceable class="parameter">column_name</replaceable> | NONE } ] @@ -111,6 +113,21 @@ <title>Parameters</title> </varlistentry> <varlistentry> + <term><replaceable class="parameter">data_type</replaceable></term> + <listitem> + <para> + The optional + clause <literal>AS <replaceable class="parameter">data_type</replaceable></literal> + specifies the data type of the sequence. Valid types are + are <literal>smallint</literal>, <literal>integer</literal>, + and <literal>bigint</literal>. <literal>bigint</literal> is the + default. The data type determines the default minimum and maximum + values of the sequence. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">increment</replaceable></term> <listitem> <para> @@ -132,9 +149,8 @@ <title>Parameters</title> class="parameter">minvalue</replaceable></literal> determines the minimum value a sequence can generate. If this clause is not supplied or <option>NO MINVALUE</option> is specified, then - defaults will be used. The defaults are 1 and - -2<superscript>63</> for ascending and descending sequences, - respectively. + defaults will be used. The default for an ascending sequence is 1. The + default for a descending sequence is the minimum value of the data type. </para> </listitem> </varlistentry> @@ -148,9 +164,9 @@ <title>Parameters</title> class="parameter">maxvalue</replaceable></literal> determines the maximum value for the sequence. If this clause is not supplied or <option>NO MAXVALUE</option> is specified, then - default values will be used. The defaults are - 2<superscript>63</>-1 and -1 for ascending and descending - sequences, respectively. + default values will be used. The default for an ascending sequence is + the maximum value of the data type. The default for a descending + sequence is -1. </para> </listitem> </varlistentry> @@ -349,12 +365,6 @@ <title>Compatibility</title> <itemizedlist> <listitem> <para> - The standard's <literal>AS <replaceable>data_type</></literal> expression is not - supported. - </para> - </listitem> - <listitem> - <para> Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression. diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 62ee2b4e0e..9a53003ecf 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -1531,8 +1531,8 @@ CREATE VIEW sequences AS SELECT CAST(current_database() AS sql_identifier) AS sequence_catalog, CAST(nc.nspname AS sql_identifier) AS sequence_schema, CAST(c.relname AS sql_identifier) AS sequence_name, - CAST('bigint' AS character_data) AS data_type, - CAST(64 AS cardinal_number) AS numeric_precision, + CAST(format_type(s.seqtypid, null) AS character_data) AS data_type, + CAST(_pg_numeric_precision(s.seqtypid, -1) AS cardinal_number) AS numeric_precision, CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(0 AS cardinal_number) AS numeric_scale, CAST(s.seqstart AS character_data) AS start_value, diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0c673f5763..624c62b427 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -33,6 +33,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_type.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/smgr.h" @@ -228,12 +229,13 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) memset(pgs_nulls, 0, sizeof(pgs_nulls)); pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid); - pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); + pgs_values[Anum_pg_sequence_seqtypid - 1] = ObjectIdGetDatum(seqform.seqtypid); pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart); pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement); pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax); pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin); pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache); + pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle); tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls); simple_heap_insert(rel, tuple); @@ -623,11 +625,11 @@ nextval_internal(Oid relid) if (!HeapTupleIsValid(pgstuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple); - cycle = pgsform->seqcycle; incby = pgsform->seqincrement; maxv = pgsform->seqmax; minv = pgsform->seqmin; cache = pgsform->seqcache; + cycle = pgsform->seqcycle; ReleaseSysCache(pgstuple); /* lock page' buffer and read tuple */ @@ -1222,6 +1224,7 @@ init_params(ParseState *pstate, List *options, bool isInit, Form_pg_sequence seqform, Form_pg_sequence_data seqdataform, List **owned_by) { + DefElem *as_type = NULL; DefElem *start_value = NULL; DefElem *restart_value = NULL; DefElem *increment_by = NULL; @@ -1237,7 +1240,16 @@ init_params(ParseState *pstate, List *options, bool isInit, { DefElem *defel = (DefElem *) lfirst(option); - if (strcmp(defel->defname, "increment") == 0) + if (strcmp(defel->defname, "as") == 0) + { + if (as_type) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"), + parser_errposition(pstate, defel->location))); + as_type = defel; + } + else if (strcmp(defel->defname, "increment") == 0) { if (increment_by) ereport(ERROR, @@ -1321,6 +1333,20 @@ init_params(ParseState *pstate, List *options, bool isInit, if (isInit) seqdataform->log_cnt = 0; + /* AS type */ + if (as_type != NULL) + { + seqform->seqtypid = typenameTypeId(pstate, defGetTypeName(as_type)); + if (seqform->seqtypid != INT2OID && + seqform->seqtypid != INT4OID && + seqform->seqtypid != INT8OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("sequence type must be smallint, integer, or bigint"))); + } + else if (isInit) + seqform->seqtypid = INT8OID; + /* INCREMENT BY */ if (increment_by != NULL) { @@ -1353,12 +1379,34 @@ init_params(ParseState *pstate, List *options, bool isInit, else if (isInit || max_value != NULL) { if (seqform->seqincrement > 0) - seqform->seqmax = PG_INT64_MAX; /* ascending seq */ + { + /* ascending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmax = PG_INT16_MAX; + else if (seqform->seqtypid == INT4OID) + seqform->seqmax = PG_INT32_MAX; + else + seqform->seqmax = PG_INT64_MAX; + } else seqform->seqmax = -1; /* descending seq */ seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmax < PG_INT16_MIN || seqform->seqmax > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmax < PG_INT32_MIN || seqform->seqmax > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmax < PG_INT64_MIN || seqform->seqmax > PG_INT64_MAX))) + { + char bufx[100]; + + snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MAXVALUE (%s) is out of range for sequence data type %s", + bufx, format_type_be(seqform->seqtypid)))); + } + /* MINVALUE (null arg means NO MINVALUE) */ if (min_value != NULL && min_value->arg) { @@ -1370,10 +1418,32 @@ init_params(ParseState *pstate, List *options, bool isInit, if (seqform->seqincrement > 0) seqform->seqmin = 1; /* ascending seq */ else - seqform->seqmin = PG_INT64_MIN; /* descending seq */ + { + /* descending seq */ + if (seqform->seqtypid == INT2OID) + seqform->seqmin = PG_INT16_MIN; + else if (seqform->seqtypid == INT4OID) + seqform->seqmin = PG_INT32_MIN; + else + seqform->seqmin = PG_INT64_MIN; + } seqdataform->log_cnt = 0; } + if ((seqform->seqtypid == INT2OID && (seqform->seqmin < PG_INT16_MIN || seqform->seqmin > PG_INT16_MAX)) + || (seqform->seqtypid == INT4OID && (seqform->seqmin < PG_INT32_MIN || seqform->seqmin > PG_INT32_MAX)) + || (seqform->seqtypid == INT8OID && (seqform->seqmin < PG_INT64_MIN || seqform->seqmin > PG_INT64_MAX))) + { + char bufm[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("MINVALUE (%s) is out of range for sequence data type %s", + bufm, format_type_be(seqform->seqtypid)))); + } + /* crosscheck min/max */ if (seqform->seqmin >= seqform->seqmax) { @@ -1591,8 +1661,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); TupleDesc tupdesc; - Datum values[6]; - bool isnull[6]; + Datum values[7]; + bool isnull[7]; HeapTuple pgstuple; Form_pg_sequence pgsform; @@ -1602,7 +1672,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", get_rel_name(relid)))); - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(7, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value", @@ -1615,6 +1685,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "data_type", + OIDOID, -1, 0); BlessTupleDesc(tupdesc); @@ -1631,6 +1703,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) values[3] = Int64GetDatum(pgsform->seqincrement); values[4] = BoolGetDatum(pgsform->seqcycle); values[5] = Int64GetDatum(pgsform->seqcache); + values[6] = ObjectIdGetDatum(pgsform->seqtypid); ReleaseSysCache(pgstuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a4edea08a3..09ac4572ab 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -3941,7 +3941,11 @@ SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { $$ = makeDefElem("cache", (Node *)$2, @1); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4e7a8c80..efb682f168 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -469,7 +469,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1)); /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a6de9d7bc2..5031d2d015 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15863,39 +15863,29 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) PGresult *res; char *startv, *incby, - *maxv = NULL, - *minv = NULL, - *cache; - char bufm[100], - bufx[100]; + *maxv, + *minv, + *cache, + *seqtype; bool cycled; + bool is_ascending; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); PQExpBuffer labelq = createPQExpBuffer(); - snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); - snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); - if (fout->remoteVersion >= 100000) { /* Make sure we are in proper schema */ selectSourceSchema(fout, "pg_catalog"); appendPQExpBuffer(query, - "SELECT seqstart, seqincrement, " - "CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL " - " WHEN seqincrement < 0 AND seqmax = -1 THEN NULL " - " ELSE seqmax " - "END AS seqmax, " - "CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL " - " WHEN seqincrement < 0 AND seqmin = %s THEN NULL " - " ELSE seqmin " - "END AS seqmin, " + "SELECT format_type(seqtypid, NULL), " + "seqstart, seqincrement, " + "seqmax, seqmin, " "seqcache, seqcycle " "FROM pg_class c " "JOIN pg_sequence s ON (s.seqrelid = c.oid) " "WHERE c.oid = '%u'::oid", - bufx, bufm, tbinfo->dobj.catId.oid); } else if (fout->remoteVersion >= 80400) @@ -15909,17 +15899,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } else @@ -15928,17 +15910,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT 0 AS start_value, increment_by, " - "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL " - " WHEN increment_by < 0 AND max_value = -1 THEN NULL " - " ELSE max_value " - "END AS max_value, " - "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL " - " WHEN increment_by < 0 AND min_value = %s THEN NULL " - " ELSE min_value " - "END AS min_value, " + "SELECT 'bigint'::name AS sequence_type, " + "0 AS start_value, increment_by, max_value, min_value, " "cache_value, is_cycled FROM %s", - bufx, bufm, fmtId(tbinfo->dobj.name)); } @@ -15953,14 +15927,48 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) exit_nicely(1); } - startv = PQgetvalue(res, 0, 0); - incby = PQgetvalue(res, 0, 1); - if (!PQgetisnull(res, 0, 2)) - maxv = PQgetvalue(res, 0, 2); - if (!PQgetisnull(res, 0, 3)) - minv = PQgetvalue(res, 0, 3); - cache = PQgetvalue(res, 0, 4); - cycled = (strcmp(PQgetvalue(res, 0, 5), "t") == 0); + seqtype = PQgetvalue(res, 0, 0); + startv = PQgetvalue(res, 0, 1); + incby = PQgetvalue(res, 0, 2); + maxv = PQgetvalue(res, 0, 3); + minv = PQgetvalue(res, 0, 4); + cache = PQgetvalue(res, 0, 5); + cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0); + + is_ascending = incby[0] != '-'; + + if (is_ascending && atoi(minv) == 1) + minv = NULL; + if (!is_ascending && atoi(maxv) == -1) + maxv = NULL; + + if (strcmp(seqtype, "smallint") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT16_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT16_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "integer") == 0) + { + if (!is_ascending && atoi(minv) == PG_INT32_MIN) + minv = NULL; + if (is_ascending && atoi(maxv) == PG_INT32_MAX) + maxv = NULL; + } + else if (strcmp(seqtype, "bigint") == 0) + { + char bufm[100], + bufx[100]; + + snprintf(bufm, sizeof(bufm), INT64_FORMAT, PG_INT64_MIN); + snprintf(bufx, sizeof(bufx), INT64_FORMAT, PG_INT64_MAX); + + if (!is_ascending && strcmp(minv, bufm) == 0) + minv = NULL; + if (is_ascending && strcmp(maxv, bufx) == 0) + maxv = NULL; + } /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -15984,6 +15992,9 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) "CREATE SEQUENCE %s\n", fmtId(tbinfo->dobj.name)); + if (strcmp(seqtype, "bigint") != 0) + appendPQExpBuffer(query, " AS %s\n", seqtype); + if (fout->remoteVersion >= 80400) appendPQExpBuffer(query, " START WITH %s\n", startv); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 488eec30f5..e73ecfae48 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -2486,6 +2486,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E @@ -2521,6 +2522,7 @@ catch_all => 'CREATE ... commands', regexp => qr/^ \QCREATE SEQUENCE test_third_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 417cfc36ec..0432b6f141 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201701251 +#define CATALOG_VERSION_NO 201701252 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 31c828a3f2..874f1a9dcf 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1766,7 +1766,7 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ )); DESCR("set sequence value and is_called status"); -DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); +DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20,26}" "{i,o,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size,data_type}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_)); DESCR("sequence parameters, for use by information schema"); DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ )); DESCR("sequence last value"); diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h index 350b286e45..ef15e68a57 100644 --- a/src/include/catalog/pg_sequence.h +++ b/src/include/catalog/pg_sequence.h @@ -8,23 +8,25 @@ CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS { Oid seqrelid; - bool seqcycle; + Oid seqtypid; int64 seqstart; int64 seqincrement; int64 seqmax; int64 seqmin; int64 seqcache; + bool seqcycle; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; -#define Natts_pg_sequence 7 +#define Natts_pg_sequence 8 #define Anum_pg_sequence_seqrelid 1 -#define Anum_pg_sequence_seqcycle 2 +#define Anum_pg_sequence_seqtypid 2 #define Anum_pg_sequence_seqstart 3 #define Anum_pg_sequence_seqincrement 4 #define Anum_pg_sequence_seqmax 5 #define Anum_pg_sequence_seqmin 6 #define Anum_pg_sequence_seqcache 7 +#define Anum_pg_sequence_seqcycle 8 #endif /* PG_SEQUENCE_H */ diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl index c8e8d4a94c..bb2a65782a 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -305,6 +305,7 @@ 'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => { regexp => qr/^ \QCREATE SEQUENCE regress_pg_dump_table_col1_seq\E + \n\s+\QAS integer\E \n\s+\QSTART WITH 1\E \n\s+\QINCREMENT BY 1\E \n\s+\QNO MINVALUE\E diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index d062e91d26..f339489151 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -28,6 +28,23 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column ERROR: column "b" of relation "sequence_test_table" does not exist DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +ERROR: sequence type must be smallint, integer, or bigint +CREATE SEQUENCE sequence_testx AS nosuchtype; +ERROR: type "nosuchtype" does not exist +LINE 1: CREATE SEQUENCE sequence_testx AS nosuchtype; + ^ +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ERROR: MAXVALUE (2147483647) is out of range for sequence data type smallint +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +ERROR: MAXVALUE (100000) is out of range for sequence data type smallint +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; +ERROR: MINVALUE (-100000) is out of range for sequence data type smallint --- --- test creation of SERIAL column --- @@ -445,13 +462,16 @@ SELECT * FROM information_schema.sequences regression | public | sequence_test2 | bigint | 64 | 2 | 0 | 32 | 5 | 36 | 4 | YES regression | public | sequence_test3 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | sequence_test4 | bigint | 64 | 2 | 0 | -1 | -9223372036854775808 | -1 | -1 | NO - regression | public | serialtest1_f2_foo | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f2_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f3_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO - regression | public | serialtest2_f4_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | sequence_test5 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test6 | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | sequence_test7 | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO + regression | public | serialtest1_f2_foo | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f2_seq | integer | 32 | 2 | 0 | 1 | 1 | 2147483647 | 1 | NO + regression | public | serialtest2_f3_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO + regression | public | serialtest2_f4_seq | smallint | 16 | 2 | 0 | 1 | 1 | 32767 | 1 | NO regression | public | serialtest2_f5_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO -(9 rows) +(12 rows) SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value FROM pg_sequences @@ -462,18 +482,21 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5 public | sequence_test3 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | public | sequence_test4 | -1 | -9223372036854775808 | -1 | -1 | f | 1 | -1 - public | serialtest1_f2_foo | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 3 - public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 - public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 + public | sequence_test5 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test6 | 1 | 1 | 32767 | 1 | f | 1 | + public | sequence_test7 | 1 | 1 | 9223372036854775807 | 1 | f | 1 | + public | serialtest1_f2_foo | 1 | 1 | 2147483647 | 1 | f | 1 | 3 + public | serialtest2_f2_seq | 1 | 1 | 2147483647 | 1 | f | 1 | 2 + public | serialtest2_f3_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 + public | serialtest2_f4_seq | 1 | 1 | 32767 | 1 | f | 1 | 2 public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2 -(9 rows) +(12 rows) SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); - start_value | minimum_value | maximum_value | increment | cycle_option | cache_size --------------+----------------------+---------------+-----------+--------------+------------ - -1 | -9223372036854775808 | -1 | -1 | f | 1 + start_value | minimum_value | maximum_value | increment | cycle_option | cache_size | data_type +-------------+----------------------+---------------+-----------+--------------+------------+----------- + -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 (1 row) -- Test comments diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql index 4b9824c3cc..0fbd255967 100644 --- a/src/test/regress/sql/sequence.sql +++ b/src/test/regress/sql/sequence.sql @@ -19,6 +19,19 @@ CREATE TABLE sequence_test_table (a int); CREATE SEQUENCE sequence_testx OWNED BY sequence_test_table.b; -- wrong column DROP TABLE sequence_test_table; +-- sequence data types +CREATE SEQUENCE sequence_test5 AS integer; +CREATE SEQUENCE sequence_test6 AS smallint; +CREATE SEQUENCE sequence_test7 AS bigint; +CREATE SEQUENCE sequence_testx AS text; +CREATE SEQUENCE sequence_testx AS nosuchtype; + +ALTER SEQUENCE sequence_test5 AS smallint; -- fails +ALTER SEQUENCE sequence_test5 AS smallint NO MINVALUE NO MAXVALUE; + +CREATE SEQUENCE sequence_testx AS smallint MAXVALUE 100000; +CREATE SEQUENCE sequence_testx AS smallint MINVALUE -100000; + --- --- test creation of SERIAL column --- -- 2.11.0 --------------9CA65CF546BB80FC2F613C34 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --------------9CA65CF546BB80FC2F613C34-- ^ permalink raw reply [nested|flat] 51+ messages in thread
* [PATCH v30 10/11] Add regression tests for Incremental View Maintenance @ 2021-03-10 02:11 Takuma Hoshiai <[email protected]> 0 siblings, 0 replies; 51+ messages in thread From: Takuma Hoshiai @ 2021-03-10 02:11 UTC (permalink / raw) --- .../regress/expected/incremental_matview.out | 1030 +++++++++++++++++ src/test/regress/parallel_schedule | 2 +- src/test/regress/sql/incremental_matview.sql | 533 +++++++++ 3 files changed, 1564 insertions(+), 1 deletion(-) create mode 100644 src/test/regress/expected/incremental_matview.out create mode 100644 src/test/regress/sql/incremental_matview.sql diff --git a/src/test/regress/expected/incremental_matview.out b/src/test/regress/expected/incremental_matview.out new file mode 100644 index 0000000000..8946d09f5d --- /dev/null +++ b/src/test/regress/expected/incremental_matview.out @@ -0,0 +1,1030 @@ +-- create a table to use as a basis for views and materialized views in various combinations +CREATE TABLE mv_base_a (i int, j int); +INSERT INTO mv_base_a VALUES + (1,10), + (2,20), + (3,30), + (4,40), + (5,50); +CREATE TABLE mv_base_b (i int, k int); +INSERT INTO mv_base_b VALUES + (1,101), + (2,102), + (3,103), + (4,104); +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_1 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) WITH NO DATA; +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; +ERROR: materialized view "mv_ivm_1" has not been populated +HINT: Use the REFRESH MATERIALIZED VIEW command. +REFRESH MATERIALIZED VIEW mv_ivm_1; +NOTICE: could not create an index on materialized view "mv_ivm_1" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; + i | j | k +---+----+----- + 1 | 10 | 101 + 2 | 20 | 102 + 3 | 30 | 103 + 4 | 40 | 104 +(4 rows) + +-- REFRESH WITH NO DATA +BEGIN; +CREATE FUNCTION dummy_ivm_trigger_func() RETURNS TRIGGER AS $$ + BEGIN + RETURN NULL; + END +$$ language plpgsql; +CREATE CONSTRAINT TRIGGER dummy_ivm_trigger AFTER INSERT +ON mv_base_a FROM mv_ivm_1 FOR EACH ROW +EXECUTE PROCEDURE dummy_ivm_trigger_func(); +SELECT COUNT(*) +FROM pg_depend pd INNER JOIN pg_trigger pt ON pd.objid = pt.oid +WHERE pd.classid = 'pg_trigger'::regclass AND pd.refobjid = 'mv_ivm_1'::regclass; + count +------- + 17 +(1 row) + +REFRESH MATERIALIZED VIEW mv_ivm_1 WITH NO DATA; +SELECT COUNT(*) +FROM pg_depend pd INNER JOIN pg_trigger pt ON pd.objid = pt.oid +WHERE pd.classid = 'pg_trigger'::regclass AND pd.refobjid = 'mv_ivm_1'::regclass; + count +------- + 1 +(1 row) + +ROLLBACK; +-- immediate maintenance +BEGIN; +INSERT INTO mv_base_b VALUES(5,105); +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; + i | j | k +---+----+----- + 1 | 10 | 101 + 2 | 20 | 102 + 3 | 30 | 103 + 4 | 40 | 104 + 5 | 50 | 105 +(5 rows) + +UPDATE mv_base_a SET j = 0 WHERE i = 1; +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; + i | j | k +---+----+----- + 1 | 0 | 101 + 2 | 20 | 102 + 3 | 30 | 103 + 4 | 40 | 104 + 5 | 50 | 105 +(5 rows) + +DELETE FROM mv_base_b WHERE (i,k) = (5,105); +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; + i | j | k +---+----+----- + 1 | 0 | 101 + 2 | 20 | 102 + 3 | 30 | 103 + 4 | 40 | 104 +(4 rows) + +ROLLBACK; +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; + i | j | k +---+----+----- + 1 | 10 | 101 + 2 | 20 | 102 + 3 | 30 | 103 + 4 | 40 | 104 +(4 rows) + +-- rename of IVM columns +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_rename AS SELECT DISTINCT * FROM mv_base_a; +NOTICE: created index "mv_ivm_rename_index" on materialized view "mv_ivm_rename" +ALTER MATERIALIZED VIEW mv_ivm_rename RENAME COLUMN __ivm_count__ TO xxx; +ERROR: IVM column can not be renamed +DROP MATERIALIZED VIEW mv_ivm_rename; +-- unique index on IVM columns +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_unique AS SELECT DISTINCT * FROM mv_base_a; +NOTICE: created index "mv_ivm_unique_index" on materialized view "mv_ivm_unique" +CREATE UNIQUE INDEX ON mv_ivm_unique(__ivm_count__); +ERROR: unique index creation on IVM columns is not supported +CREATE UNIQUE INDEX ON mv_ivm_unique((__ivm_count__)); +ERROR: unique index creation on IVM columns is not supported +CREATE UNIQUE INDEX ON mv_ivm_unique((__ivm_count__ + 1)); +ERROR: unique index creation on IVM columns is not supported +DROP MATERIALIZED VIEW mv_ivm_unique; +-- TRUNCATE a base table in join views +BEGIN; +TRUNCATE mv_base_a; +SELECT * FROM mv_ivm_1; + i | j | k +---+---+--- +(0 rows) + +ROLLBACK; +BEGIN; +TRUNCATE mv_base_b; +SELECT * FROM mv_ivm_1; + i | j | k +---+---+--- +(0 rows) + +ROLLBACK; +-- some query syntax +BEGIN; +CREATE FUNCTION ivm_func() RETURNS int LANGUAGE 'sql' + AS 'SELECT 1' IMMUTABLE; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_func AS SELECT * FROM ivm_func(); +NOTICE: could not create an index on materialized view "mv_ivm_func" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_no_tbl AS SELECT 1; +NOTICE: could not create an index on materialized view "mv_ivm_no_tbl" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +ROLLBACK; +-- result of materialized view have DISTINCT clause or the duplicate result. +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_duplicate AS SELECT j FROM mv_base_a; +NOTICE: could not create an index on materialized view "mv_ivm_duplicate" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_distinct AS SELECT DISTINCT j FROM mv_base_a; +NOTICE: created index "mv_ivm_distinct_index" on materialized view "mv_ivm_distinct" +INSERT INTO mv_base_a VALUES(6,20); +SELECT * FROM mv_ivm_duplicate ORDER BY 1; + j +---- + 10 + 20 + 20 + 30 + 40 + 50 +(6 rows) + +SELECT * FROM mv_ivm_distinct ORDER BY 1; + j +---- + 10 + 20 + 30 + 40 + 50 +(5 rows) + +DELETE FROM mv_base_a WHERE (i,j) = (2,20); +SELECT * FROM mv_ivm_duplicate ORDER BY 1; + j +---- + 10 + 20 + 30 + 40 + 50 +(5 rows) + +SELECT * FROM mv_ivm_distinct ORDER BY 1; + j +---- + 10 + 20 + 30 + 40 + 50 +(5 rows) + +ROLLBACK; +-- support SUM(), COUNT() and AVG() aggregate functions +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg AS SELECT i, SUM(j), COUNT(i), AVG(j) FROM mv_base_a GROUP BY i; +NOTICE: created index "mv_ivm_agg_index" on materialized view "mv_ivm_agg" +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3,4; + i | sum | count | avg +---+-----+-------+--------------------- + 1 | 10 | 1 | 10.0000000000000000 + 2 | 20 | 1 | 20.0000000000000000 + 3 | 30 | 1 | 30.0000000000000000 + 4 | 40 | 1 | 40.0000000000000000 + 5 | 50 | 1 | 50.0000000000000000 +(5 rows) + +INSERT INTO mv_base_a VALUES(2,100); +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3,4; + i | sum | count | avg +---+-----+-------+--------------------- + 1 | 10 | 1 | 10.0000000000000000 + 2 | 120 | 2 | 60.0000000000000000 + 3 | 30 | 1 | 30.0000000000000000 + 4 | 40 | 1 | 40.0000000000000000 + 5 | 50 | 1 | 50.0000000000000000 +(5 rows) + +UPDATE mv_base_a SET j = 200 WHERE (i,j) = (2,100); +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3,4; + i | sum | count | avg +---+-----+-------+---------------------- + 1 | 10 | 1 | 10.0000000000000000 + 2 | 220 | 2 | 110.0000000000000000 + 3 | 30 | 1 | 30.0000000000000000 + 4 | 40 | 1 | 40.0000000000000000 + 5 | 50 | 1 | 50.0000000000000000 +(5 rows) + +DELETE FROM mv_base_a WHERE (i,j) = (2,200); +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3,4; + i | sum | count | avg +---+-----+-------+--------------------- + 1 | 10 | 1 | 10.0000000000000000 + 2 | 20 | 1 | 20.0000000000000000 + 3 | 30 | 1 | 30.0000000000000000 + 4 | 40 | 1 | 40.0000000000000000 + 5 | 50 | 1 | 50.0000000000000000 +(5 rows) + +ROLLBACK; +-- support COUNT(*) aggregate function +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg AS SELECT i, SUM(j), COUNT(*) FROM mv_base_a GROUP BY i; +NOTICE: created index "mv_ivm_agg_index" on materialized view "mv_ivm_agg" +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3; + i | sum | count +---+-----+------- + 1 | 10 | 1 + 2 | 20 | 1 + 3 | 30 | 1 + 4 | 40 | 1 + 5 | 50 | 1 +(5 rows) + +INSERT INTO mv_base_a VALUES(2,100); +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3; + i | sum | count +---+-----+------- + 1 | 10 | 1 + 2 | 120 | 2 + 3 | 30 | 1 + 4 | 40 | 1 + 5 | 50 | 1 +(5 rows) + +ROLLBACK; +-- TRUNCATE a base table in aggregate views +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg AS SELECT i, SUM(j), COUNT(*) FROM mv_base_a GROUP BY i; +NOTICE: created index "mv_ivm_agg_index" on materialized view "mv_ivm_agg" +TRUNCATE mv_base_a; +SELECT sum, count FROM mv_ivm_agg; + sum | count +-----+------- +(0 rows) + +SELECT i, SUM(j), COUNT(*) FROM mv_base_a GROUP BY i; + i | sum | count +---+-----+------- +(0 rows) + +ROLLBACK; +-- support aggregate functions without GROUP clause +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_group AS SELECT SUM(j), COUNT(j), AVG(j) FROM mv_base_a; +NOTICE: could not create an index on materialized view "mv_ivm_group" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT * FROM mv_ivm_group ORDER BY 1; + sum | count | avg +-----+-------+--------------------- + 150 | 5 | 30.0000000000000000 +(1 row) + +INSERT INTO mv_base_a VALUES(6,60); +SELECT * FROM mv_ivm_group ORDER BY 1; + sum | count | avg +-----+-------+--------------------- + 210 | 6 | 35.0000000000000000 +(1 row) + +DELETE FROM mv_base_a; +SELECT * FROM mv_ivm_group ORDER BY 1; + sum | count | avg +-----+-------+----- + | 0 | +(1 row) + +ROLLBACK; +-- TRUNCATE a base table in aggregate views without GROUP clause +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_group AS SELECT SUM(j), COUNT(j), AVG(j) FROM mv_base_a; +NOTICE: could not create an index on materialized view "mv_ivm_group" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +TRUNCATE mv_base_a; +SELECT sum, count, avg FROM mv_ivm_group; + sum | count | avg +-----+-------+----- + | 0 | +(1 row) + +SELECT SUM(j), COUNT(j), AVG(j) FROM mv_base_a; + sum | count | avg +-----+-------+----- + | 0 | +(1 row) + +ROLLBACK; +-- resolved issue: When use AVG() function and values is indivisible, result of AVG() is incorrect. +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_avg_bug AS SELECT i, SUM(j), COUNT(j), AVG(j) FROM mv_base_A GROUP BY i; +NOTICE: created index "mv_ivm_avg_bug_index" on materialized view "mv_ivm_avg_bug" +SELECT * FROM mv_ivm_avg_bug ORDER BY 1,2,3; + i | sum | count | avg +---+-----+-------+--------------------- + 1 | 10 | 1 | 10.0000000000000000 + 2 | 20 | 1 | 20.0000000000000000 + 3 | 30 | 1 | 30.0000000000000000 + 4 | 40 | 1 | 40.0000000000000000 + 5 | 50 | 1 | 50.0000000000000000 +(5 rows) + +INSERT INTO mv_base_a VALUES + (1,0), + (1,0), + (2,30), + (2,30); +SELECT * FROM mv_ivm_avg_bug ORDER BY 1,2,3; + i | sum | count | avg +---+-----+-------+--------------------- + 1 | 10 | 3 | 3.3333333333333333 + 2 | 80 | 3 | 26.6666666666666667 + 3 | 30 | 1 | 30.0000000000000000 + 4 | 40 | 1 | 40.0000000000000000 + 5 | 50 | 1 | 50.0000000000000000 +(5 rows) + +DELETE FROM mv_base_a WHERE (i,j) = (1,0); +DELETE FROM mv_base_a WHERE (i,j) = (2,30); +SELECT * FROM mv_ivm_avg_bug ORDER BY 1,2,3; + i | sum | count | avg +---+-----+-------+--------------------- + 1 | 10 | 1 | 10.0000000000000000 + 2 | 20 | 1 | 20.0000000000000000 + 3 | 30 | 1 | 30.0000000000000000 + 4 | 40 | 1 | 40.0000000000000000 + 5 | 50 | 1 | 50.0000000000000000 +(5 rows) + +ROLLBACK; +-- support MIN(), MAX() aggregate functions +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_min_max AS SELECT i, MIN(j), MAX(j) FROM mv_base_a GROUP BY i; +NOTICE: created index "mv_ivm_min_max_index" on materialized view "mv_ivm_min_max" +SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3; + i | min | max +---+-----+----- + 1 | 10 | 10 + 2 | 20 | 20 + 3 | 30 | 30 + 4 | 40 | 40 + 5 | 50 | 50 +(5 rows) + +INSERT INTO mv_base_a VALUES + (1,11), (1,12), + (2,21), (2,22), + (3,31), (3,32), + (4,41), (4,42), + (5,51), (5,52); +SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3; + i | min | max +---+-----+----- + 1 | 10 | 12 + 2 | 20 | 22 + 3 | 30 | 32 + 4 | 40 | 42 + 5 | 50 | 52 +(5 rows) + +DELETE FROM mv_base_a WHERE (i,j) IN ((1,10), (2,21), (3,32)); +SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3; + i | min | max +---+-----+----- + 1 | 11 | 12 + 2 | 20 | 22 + 3 | 30 | 31 + 4 | 40 | 42 + 5 | 50 | 52 +(5 rows) + +ROLLBACK; +-- support MIN(), MAX() aggregate functions without GROUP clause +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_min_max AS SELECT MIN(j), MAX(j) FROM mv_base_a; +NOTICE: could not create an index on materialized view "mv_ivm_min_max" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT * FROM mv_ivm_min_max; + min | max +-----+----- + 10 | 50 +(1 row) + +INSERT INTO mv_base_a VALUES + (0,0), (6,60), (7,70); +SELECT * FROM mv_ivm_min_max; + min | max +-----+----- + 0 | 70 +(1 row) + +DELETE FROM mv_base_a WHERE (i,j) IN ((0,0), (7,70)); +SELECT * FROM mv_ivm_min_max; + min | max +-----+----- + 10 | 60 +(1 row) + +DELETE FROM mv_base_a; +SELECT * FROM mv_ivm_min_max; + min | max +-----+----- + | +(1 row) + +ROLLBACK; +-- Test MIN/MAX after search_path change +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_min AS SELECT MIN(j) FROM mv_base_a; +NOTICE: could not create an index on materialized view "mv_ivm_min" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT * FROM mv_ivm_min; + min +----- + 10 +(1 row) + +CREATE SCHEMA myschema; +GRANT ALL ON SCHEMA myschema TO public; +CREATE TABLE myschema.mv_base_a (j int); +INSERT INTO myschema.mv_base_a VALUES (1); +DELETE FROM mv_base_a WHERE (i,j) = (1,10); +SELECT * FROM mv_ivm_min; + min +----- + 20 +(1 row) + +SET search_path TO myschema,public,pg_catalog; +DELETE FROM public.mv_base_a WHERE (i,j) = (2,20); +SELECT * FROM mv_ivm_min; + min +----- + 30 +(1 row) + +ROLLBACK; +-- aggregate views with column names specified +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg(a) AS SELECT i, SUM(j) FROM mv_base_a GROUP BY i; +NOTICE: created index "mv_ivm_agg_index" on materialized view "mv_ivm_agg" +INSERT INTO mv_base_a VALUES (1,100), (2,200), (3,300); +UPDATE mv_base_a SET j = 2000 WHERE (i,j) = (2,20); +DELETE FROM mv_base_a WHERE (i,j) = (3,30); +SELECT * FROM mv_ivm_agg ORDER BY 1,2; + a | sum +---+------ + 1 | 110 + 2 | 2200 + 3 | 300 + 4 | 40 + 5 | 50 +(5 rows) + +ROLLBACK; +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg(a,b) AS SELECT i, SUM(j) FROM mv_base_a GROUP BY i; +NOTICE: created index "mv_ivm_agg_index" on materialized view "mv_ivm_agg" +INSERT INTO mv_base_a VALUES (1,100), (2,200), (3,300); +UPDATE mv_base_a SET j = 2000 WHERE (i,j) = (2,20); +DELETE FROM mv_base_a WHERE (i,j) = (3,30); +SELECT * FROM mv_ivm_agg ORDER BY 1,2; + a | b +---+------ + 1 | 110 + 2 | 2200 + 3 | 300 + 4 | 40 + 5 | 50 +(5 rows) + +ROLLBACK; +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg(a,b,c) AS SELECT i, SUM(j) FROM mv_base_a GROUP BY i; +ERROR: too many column names were specified +ROLLBACK; +-- support self join view and multiple change on the same table +BEGIN; +CREATE TABLE base_t (i int, v int); +INSERT INTO base_t VALUES (1, 10), (2, 20), (3, 30); +CREATE INCREMENTAL MATERIALIZED VIEW mv_self(v1, v2) AS + SELECT t1.v, t2.v FROM base_t AS t1 JOIN base_t AS t2 ON t1.i = t2.i; +NOTICE: could not create an index on materialized view "mv_self" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT * FROM mv_self ORDER BY v1; + v1 | v2 +----+---- + 10 | 10 + 20 | 20 + 30 | 30 +(3 rows) + +INSERT INTO base_t VALUES (4,40); +DELETE FROM base_t WHERE i = 1; +UPDATE base_t SET v = v*10 WHERE i=2; +SELECT * FROM mv_self ORDER BY v1; + v1 | v2 +-----+----- + 30 | 30 + 40 | 40 + 200 | 200 +(3 rows) + +WITH + ins_t1 AS (INSERT INTO base_t VALUES (5,50) RETURNING 1), + ins_t2 AS (INSERT INTO base_t VALUES (6,60) RETURNING 1), + upd_t AS (UPDATE base_t SET v = v + 100 RETURNING 1), + dlt_t AS (DELETE FROM base_t WHERE i IN (4,5) RETURNING 1) +SELECT NULL; + ?column? +---------- + +(1 row) + +SELECT * FROM mv_self ORDER BY v1; + v1 | v2 +-----+----- + 50 | 50 + 60 | 60 + 130 | 130 + 300 | 300 +(4 rows) + +--- with sub-transactions +SAVEPOINT p1; +INSERT INTO base_t VALUES (7,70); +RELEASE SAVEPOINT p1; +INSERT INTO base_t VALUES (7,77); +SELECT * FROM mv_self ORDER BY v1, v2; + v1 | v2 +-----+----- + 50 | 50 + 60 | 60 + 70 | 70 + 70 | 77 + 77 | 70 + 77 | 77 + 130 | 130 + 300 | 300 +(8 rows) + +ROLLBACK; +-- support simultaneous table changes +BEGIN; +CREATE TABLE base_r (i int, v int); +CREATE TABLE base_s (i int, v int); +INSERT INTO base_r VALUES (1, 10), (2, 20), (3, 30); +INSERT INTO base_s VALUES (1, 100), (2, 200), (3, 300); +CREATE INCREMENTAL MATERIALIZED VIEW mv(v1, v2) AS + SELECT r.v, s.v FROM base_r AS r JOIN base_s AS s USING(i); +NOTICE: could not create an index on materialized view "mv" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT * FROM mv ORDER BY v1; + v1 | v2 +----+----- + 10 | 100 + 20 | 200 + 30 | 300 +(3 rows) + +WITH + ins_r AS (INSERT INTO base_r VALUES (1,11) RETURNING 1), + ins_r2 AS (INSERT INTO base_r VALUES (3,33) RETURNING 1), + ins_s AS (INSERT INTO base_s VALUES (2,222) RETURNING 1), + upd_r AS (UPDATE base_r SET v = v + 1000 WHERE i = 2 RETURNING 1), + dlt_s AS (DELETE FROM base_s WHERE i = 3 RETURNING 1) +SELECT NULL; + ?column? +---------- + +(1 row) + +SELECT * FROM mv ORDER BY v1; + v1 | v2 +------+----- + 10 | 100 + 11 | 100 + 1020 | 200 + 1020 | 222 +(4 rows) + +ROLLBACK; +-- support foreign reference constraints +BEGIN; +CREATE TABLE ri1 (i int PRIMARY KEY); +CREATE TABLE ri2 (i int PRIMARY KEY REFERENCES ri1(i) ON UPDATE CASCADE ON DELETE CASCADE, v int); +INSERT INTO ri1 VALUES (1),(2),(3); +INSERT INTO ri2 VALUES (1),(2),(3); +CREATE INCREMENTAL MATERIALIZED VIEW mv_ri(i1, i2) AS + SELECT ri1.i, ri2.i FROM ri1 JOIN ri2 USING(i); +NOTICE: created index "mv_ri_index" on materialized view "mv_ri" +SELECT * FROM mv_ri ORDER BY i1; + i1 | i2 +----+---- + 1 | 1 + 2 | 2 + 3 | 3 +(3 rows) + +UPDATE ri1 SET i=10 where i=1; +DELETE FROM ri1 WHERE i=2; +SELECT * FROM mv_ri ORDER BY i2; + i1 | i2 +----+---- + 3 | 3 + 10 | 10 +(2 rows) + +ROLLBACK; +-- views including NULL +BEGIN; +CREATE TABLE base_t (i int, v int); +INSERT INTO base_t VALUES (1,10),(2, NULL); +CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT * FROM base_t; +NOTICE: could not create an index on materialized view "mv" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT * FROM mv ORDER BY i; + i | v +---+---- + 1 | 10 + 2 | +(2 rows) + +UPDATE base_t SET v = 20 WHERE i = 2; +SELECT * FROM mv ORDER BY i; + i | v +---+---- + 1 | 10 + 2 | 20 +(2 rows) + +ROLLBACK; +BEGIN; +CREATE TABLE base_t (i int); +CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT * FROM base_t; +NOTICE: could not create an index on materialized view "mv" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT * FROM mv ORDER BY i; + i +--- +(0 rows) + +INSERT INTO base_t VALUES (1),(NULL); +SELECT * FROM mv ORDER BY i; + i +--- + 1 + +(2 rows) + +ROLLBACK; +BEGIN; +CREATE TABLE base_t (i int, v int); +INSERT INTO base_t VALUES (NULL, 1), (NULL, 2), (1, 10), (1, 20); +CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT i, sum(v) FROM base_t GROUP BY i; +NOTICE: created index "mv_index" on materialized view "mv" +SELECT * FROM mv ORDER BY i; + i | sum +---+----- + 1 | 30 + | 3 +(2 rows) + +UPDATE base_t SET v = v * 10; +SELECT * FROM mv ORDER BY i; + i | sum +---+----- + 1 | 300 + | 30 +(2 rows) + +ROLLBACK; +BEGIN; +CREATE TABLE base_t (i int, v int); +INSERT INTO base_t VALUES (NULL, 1), (NULL, 2), (NULL, 3), (NULL, 4), (NULL, 5); +CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT i, min(v), max(v) FROM base_t GROUP BY i; +NOTICE: created index "mv_index" on materialized view "mv" +SELECT * FROM mv ORDER BY i; + i | min | max +---+-----+----- + | 1 | 5 +(1 row) + +DELETE FROM base_t WHERE v = 1; +SELECT * FROM mv ORDER BY i; + i | min | max +---+-----+----- + | 2 | 5 +(1 row) + +DELETE FROM base_t WHERE v = 3; +SELECT * FROM mv ORDER BY i; + i | min | max +---+-----+----- + | 2 | 5 +(1 row) + +DELETE FROM base_t WHERE v = 5; +SELECT * FROM mv ORDER BY i; + i | min | max +---+-----+----- + | 2 | 4 +(1 row) + +ROLLBACK; +-- IMMV containing user defined type +BEGIN; +CREATE TYPE mytype; +CREATE FUNCTION mytype_in(cstring) + RETURNS mytype AS 'int4in' + LANGUAGE INTERNAL STRICT IMMUTABLE; +NOTICE: return type mytype is only a shell +CREATE FUNCTION mytype_out(mytype) + RETURNS cstring AS 'int4out' + LANGUAGE INTERNAL STRICT IMMUTABLE; +NOTICE: argument type mytype is only a shell +CREATE TYPE mytype ( + LIKE = int4, + INPUT = mytype_in, + OUTPUT = mytype_out +); +CREATE FUNCTION mytype_eq(mytype, mytype) + RETURNS bool AS 'int4eq' + LANGUAGE INTERNAL STRICT IMMUTABLE; +CREATE FUNCTION mytype_lt(mytype, mytype) + RETURNS bool AS 'int4lt' + LANGUAGE INTERNAL STRICT IMMUTABLE; +CREATE FUNCTION mytype_cmp(mytype, mytype) + RETURNS integer AS 'btint4cmp' + LANGUAGE INTERNAL STRICT IMMUTABLE; +CREATE OPERATOR = ( + leftarg = mytype, rightarg = mytype, + procedure = mytype_eq); +CREATE OPERATOR < ( + leftarg = mytype, rightarg = mytype, + procedure = mytype_lt); +CREATE OPERATOR CLASS mytype_ops + DEFAULT FOR TYPE mytype USING btree AS + OPERATOR 1 <, + OPERATOR 3 = , + FUNCTION 1 mytype_cmp(mytype,mytype); +CREATE TABLE t_mytype (x mytype); +CREATE INCREMENTAL MATERIALIZED VIEW mv_mytype AS + SELECT * FROM t_mytype; +NOTICE: could not create an index on materialized view "mv_mytype" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +INSERT INTO t_mytype VALUES ('1'::mytype); +SELECT * FROM mv_mytype; + x +--- + 1 +(1 row) + +ROLLBACK; +-- outer join is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv(a,b) AS SELECT a.i, b.i FROM mv_base_a a LEFT JOIN mv_base_b b ON a.i=b.i; +ERROR: OUTER JOIN is not supported on incrementally maintainable materialized view +-- CTE is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv AS + WITH b AS ( SELECT * FROM mv_base_b) SELECT a.i,a.j FROM mv_base_a a, b WHERE a.i = b.i; +ERROR: CTE is not supported on incrementally maintainable materialized view +-- contain system column +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm01 AS SELECT i,j,xmin FROM mv_base_a; +ERROR: system column is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm02 AS SELECT i,j FROM mv_base_a WHERE xmin = '610'; +ERROR: system column is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm04 AS SELECT i,j,xmin::text AS x_min FROM mv_base_a; +ERROR: system column is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm06 AS SELECT i,j,xidsend(xmin) AS x_min FROM mv_base_a; +ERROR: system column is not supported on incrementally maintainable materialized view +-- contain subquery +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm03 AS SELECT i,j FROM mv_base_a WHERE i IN (SELECT i FROM mv_base_b WHERE k < 103 ); +ERROR: subquery is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm04 AS SELECT a.i,a.j FROM mv_base_a a, (SELECT * FROM mv_base_b) b WHERE a.i = b.i; +ERROR: subquery is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm05 AS SELECT i,j, (SELECT k FROM mv_base_b b WHERE a.i = b.i) FROM mv_base_a a; +ERROR: subquery is not supported on incrementally maintainable materialized view +-- contain ORDER BY +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm07 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) ORDER BY i,j,k; +ERROR: ORDER BY clause is not supported on incrementally maintainable materialized view +-- contain HAVING +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm08 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) GROUP BY i,j,k HAVING SUM(i) > 5; +ERROR: HAVING clause is not supported on incrementally maintainable materialized view +-- contain view or materialized view +CREATE VIEW b_view AS SELECT i,k FROM mv_base_b; +CREATE MATERIALIZED VIEW b_mview AS SELECT i,k FROM mv_base_b; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm07 AS SELECT a.i,a.j FROM mv_base_a a,b_view b WHERE a.i = b.i; +ERROR: VIEW or MATERIALIZED VIEW is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm08 AS SELECT a.i,a.j FROM mv_base_a a,b_mview b WHERE a.i = b.i; +ERROR: VIEW or MATERIALIZED VIEW is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm09 AS SELECT a.i,a.j FROM mv_base_a a, (SELECT i, COUNT(*) FROM mv_base_b GROUP BY i) b WHERE a.i = b.i; +ERROR: subquery is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm10 AS SELECT a.i,a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i) OR a.i > 5; +ERROR: subquery is not supported on incrementally maintainable materialized view +-- contain mutable functions +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm12 AS SELECT i,j FROM mv_base_a WHERE i = random()::int; +ERROR: mutable function is not supported on incrementally maintainable materialized view +HINT: functions must be marked IMMUTABLE +-- LIMIT/OFFSET is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm13 AS SELECT i,j FROM mv_base_a LIMIT 10 OFFSET 5; +ERROR: LIMIT/OFFSET clause is not supported on incrementally maintainable materialized view +-- DISTINCT ON is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm14 AS SELECT DISTINCT ON(i) i, j FROM mv_base_a; +ERROR: DISTINCT ON is not supported on incrementally maintainable materialized view +-- TABLESAMPLE clause is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm15 AS SELECT i, j FROM mv_base_a TABLESAMPLE SYSTEM(50); +ERROR: TABLESAMPLE clause is not supported on incrementally maintainable materialized view +-- window functions are not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm16 AS SELECT *, cume_dist() OVER (ORDER BY i) AS rank FROM mv_base_a; +ERROR: window functions are not supported on incrementally maintainable materialized view +-- aggregate function with some options is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm17 AS SELECT COUNT(*) FILTER(WHERE i < 3) FROM mv_base_a; +ERROR: aggregate function with FILTER clause is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm18 AS SELECT COUNT(DISTINCT i) FROM mv_base_a; +ERROR: aggregate function with DISTINCT arguments is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm19 AS SELECT array_agg(j ORDER BY i DESC) FROM mv_base_a; +ERROR: aggregate function with ORDER clause is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm20 AS SELECT i,SUM(j) FROM mv_base_a GROUP BY GROUPING SETS((i),()); +ERROR: GROUPING SETS, ROLLUP, or CUBE clauses is not supported on incrementally maintainable materialized view +-- inheritance parent is not supported +BEGIN; +CREATE TABLE parent (i int, v int); +CREATE TABLE child_a(options text) INHERITS(parent); +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm21 AS SELECT * FROM parent; +ERROR: inheritance parent is not supported on incrementally maintainable materialized view +ROLLBACK; +-- UNION statement is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm22 AS SELECT i,j FROM mv_base_a UNION ALL SELECT i,k FROM mv_base_b;; +ERROR: UNION/INTERSECT/EXCEPT statements are not supported on incrementally maintainable materialized view +-- empty target list is not allowed with IVM +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm25 AS SELECT FROM mv_base_a; +ERROR: empty target list is not supported on incrementally maintainable materialized view +-- FOR UPDATE/SHARE is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm26 AS SELECT i,j FROM mv_base_a FOR UPDATE; +ERROR: FOR UPDATE/SHARE clause is not supported on incrementally maintainable materialized view +-- tartget list cannot contain ivm column that start with '__ivm' +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm28 AS SELECT i AS "__ivm_count__" FROM mv_base_a; +ERROR: column name __ivm_count__ is not supported on incrementally maintainable materialized view +-- expressions specified in GROUP BY must appear in the target list. +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm29 AS SELECT COUNT(i) FROM mv_base_a GROUP BY i; +ERROR: GROUP BY expression not appearing in select list is not supported on incrementally maintainable materialized view +-- experssions containing an aggregate is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm30 AS SELECT sum(i)*0.5 FROM mv_base_a; +ERROR: expression containing an aggregate in it is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm31 AS SELECT sum(i)/sum(j) FROM mv_base_a; +ERROR: expression containing an aggregate in it is not supported on incrementally maintainable materialized view +-- VALUES is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_only_values1 AS values(1); +ERROR: VALUES is not supported on incrementally maintainable materialized view +-- views containing base tables with Row Level Security +DROP USER IF EXISTS ivm_admin; +NOTICE: role "ivm_admin" does not exist, skipping +DROP USER IF EXISTS ivm_user; +NOTICE: role "ivm_user" does not exist, skipping +CREATE USER ivm_admin; +CREATE USER ivm_user; +--- create a table with RLS +SET SESSION AUTHORIZATION ivm_admin; +CREATE TABLE rls_tbl(id int, data text, owner name); +INSERT INTO rls_tbl VALUES + (1,'foo','ivm_user'), + (2,'bar','postgres'); +CREATE TABLE num_tbl(id int, num text); +INSERT INTO num_tbl VALUES + (1,'one'), + (2,'two'), + (3,'three'), + (4,'four'), + (5,'five'), + (6,'six'); +--- Users can access only their own rows +CREATE POLICY rls_tbl_policy ON rls_tbl FOR SELECT TO PUBLIC USING(owner = current_user); +ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY; +GRANT ALL on rls_tbl TO PUBLIC; +GRANT ALL on num_tbl TO PUBLIC; +--- create a view owned by ivm_user +SET SESSION AUTHORIZATION ivm_user; +CREATE INCREMENTAL MATERIALIZED VIEW ivm_rls AS SELECT * FROM rls_tbl; +NOTICE: could not create an index on materialized view "ivm_rls" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3; + id | data | owner +----+------+---------- + 1 | foo | ivm_user +(1 row) + +RESET SESSION AUTHORIZATION; +--- inserts rows owned by different users +INSERT INTO rls_tbl VALUES + (3,'baz','ivm_user'), + (4,'qux','postgres'); +SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3; + id | data | owner +----+------+---------- + 1 | foo | ivm_user + 3 | baz | ivm_user +(2 rows) + +--- combination of diffent kinds of commands +WITH + i AS (INSERT INTO rls_tbl VALUES(5,'quux','postgres'), (6,'corge','ivm_user')), + u AS (UPDATE rls_tbl SET owner = 'postgres' WHERE id = 1), + u2 AS (UPDATE rls_tbl SET owner = 'ivm_user' WHERE id = 2) +SELECT; +-- +(1 row) + +SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3; + id | data | owner +----+-------+---------- + 2 | bar | ivm_user + 3 | baz | ivm_user + 6 | corge | ivm_user +(3 rows) + +--- +SET SESSION AUTHORIZATION ivm_user; +CREATE INCREMENTAL MATERIALIZED VIEW ivm_rls2 AS SELECT * FROM rls_tbl JOIN num_tbl USING(id); +NOTICE: could not create an index on materialized view "ivm_rls2" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +RESET SESSION AUTHORIZATION; +WITH + x AS (UPDATE rls_tbl SET data = data || '_2' where id in (3,4)), + y AS (UPDATE num_tbl SET num = num || '_2' where id in (3,4)) +SELECT; +-- +(1 row) + +SELECT * FROM ivm_rls2 ORDER BY 1,2,3; + id | data | owner | num +----+-------+----------+--------- + 2 | bar | ivm_user | two + 3 | baz_2 | ivm_user | three_2 + 6 | corge | ivm_user | six +(3 rows) + +-- automatic index creation +CREATE TABLE base_a (i int primary key, j int); +CREATE TABLE base_b (i int primary key, j int); +--- group by: create an index +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx1 AS SELECT i, sum(j) FROM base_a GROUP BY i; +NOTICE: created index "mv_idx1_index" on materialized view "mv_idx1" +--- distinct: create an index +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx2 AS SELECT DISTINCT j FROM base_a; +NOTICE: created index "mv_idx2_index" on materialized view "mv_idx2" +--- with all pkey columns: create an index +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx3(i_a, i_b) AS SELECT a.i, b.i FROM base_a a, base_b b; +NOTICE: created index "mv_idx3_index" on materialized view "mv_idx3" +--- missing some pkey columns: no index +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx4 AS SELECT j FROM base_a; +NOTICE: could not create an index on materialized view "mv_idx4" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx5 AS SELECT a.i, b.j FROM base_a a, base_b b; +NOTICE: could not create an index on materialized view "mv_idx5" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +-- cleanup +DROP TABLE rls_tbl CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to materialized view ivm_rls +drop cascades to materialized view ivm_rls2 +DROP TABLE num_tbl CASCADE; +DROP USER ivm_user; +DROP USER ivm_admin; +DROP TABLE mv_base_b CASCADE; +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to materialized view mv_ivm_1 +drop cascades to view b_view +drop cascades to materialized view b_mview +DROP TABLE mv_base_a CASCADE; diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 1d8a414eea..84c3c5e9be 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -78,7 +78,7 @@ test: brin_bloom brin_multi # psql depends on create_am # amutils depends on geometry, create_index_spgist, hash_index, brin # ---------- -test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role without_overlaps +test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role without_overlaps incremental_matview # collate.*.utf8 tests cannot be run in parallel with each other test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252 diff --git a/src/test/regress/sql/incremental_matview.sql b/src/test/regress/sql/incremental_matview.sql new file mode 100644 index 0000000000..82686f9324 --- /dev/null +++ b/src/test/regress/sql/incremental_matview.sql @@ -0,0 +1,533 @@ +-- create a table to use as a basis for views and materialized views in various combinations +CREATE TABLE mv_base_a (i int, j int); +INSERT INTO mv_base_a VALUES + (1,10), + (2,20), + (3,30), + (4,40), + (5,50); +CREATE TABLE mv_base_b (i int, k int); +INSERT INTO mv_base_b VALUES + (1,101), + (2,102), + (3,103), + (4,104); + +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_1 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) WITH NO DATA; +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; +REFRESH MATERIALIZED VIEW mv_ivm_1; +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; + +-- REFRESH WITH NO DATA +BEGIN; +CREATE FUNCTION dummy_ivm_trigger_func() RETURNS TRIGGER AS $$ + BEGIN + RETURN NULL; + END +$$ language plpgsql; + +CREATE CONSTRAINT TRIGGER dummy_ivm_trigger AFTER INSERT +ON mv_base_a FROM mv_ivm_1 FOR EACH ROW +EXECUTE PROCEDURE dummy_ivm_trigger_func(); + +SELECT COUNT(*) +FROM pg_depend pd INNER JOIN pg_trigger pt ON pd.objid = pt.oid +WHERE pd.classid = 'pg_trigger'::regclass AND pd.refobjid = 'mv_ivm_1'::regclass; + +REFRESH MATERIALIZED VIEW mv_ivm_1 WITH NO DATA; + +SELECT COUNT(*) +FROM pg_depend pd INNER JOIN pg_trigger pt ON pd.objid = pt.oid +WHERE pd.classid = 'pg_trigger'::regclass AND pd.refobjid = 'mv_ivm_1'::regclass; +ROLLBACK; + +-- immediate maintenance +BEGIN; +INSERT INTO mv_base_b VALUES(5,105); +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; +UPDATE mv_base_a SET j = 0 WHERE i = 1; +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; +DELETE FROM mv_base_b WHERE (i,k) = (5,105); +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; +ROLLBACK; +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; + +-- rename of IVM columns +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_rename AS SELECT DISTINCT * FROM mv_base_a; +ALTER MATERIALIZED VIEW mv_ivm_rename RENAME COLUMN __ivm_count__ TO xxx; +DROP MATERIALIZED VIEW mv_ivm_rename; + +-- unique index on IVM columns +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_unique AS SELECT DISTINCT * FROM mv_base_a; +CREATE UNIQUE INDEX ON mv_ivm_unique(__ivm_count__); +CREATE UNIQUE INDEX ON mv_ivm_unique((__ivm_count__)); +CREATE UNIQUE INDEX ON mv_ivm_unique((__ivm_count__ + 1)); +DROP MATERIALIZED VIEW mv_ivm_unique; + +-- TRUNCATE a base table in join views +BEGIN; +TRUNCATE mv_base_a; +SELECT * FROM mv_ivm_1; +ROLLBACK; + +BEGIN; +TRUNCATE mv_base_b; +SELECT * FROM mv_ivm_1; +ROLLBACK; + +-- some query syntax +BEGIN; +CREATE FUNCTION ivm_func() RETURNS int LANGUAGE 'sql' + AS 'SELECT 1' IMMUTABLE; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_func AS SELECT * FROM ivm_func(); +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_no_tbl AS SELECT 1; +ROLLBACK; + +-- result of materialized view have DISTINCT clause or the duplicate result. +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_duplicate AS SELECT j FROM mv_base_a; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_distinct AS SELECT DISTINCT j FROM mv_base_a; +INSERT INTO mv_base_a VALUES(6,20); +SELECT * FROM mv_ivm_duplicate ORDER BY 1; +SELECT * FROM mv_ivm_distinct ORDER BY 1; +DELETE FROM mv_base_a WHERE (i,j) = (2,20); +SELECT * FROM mv_ivm_duplicate ORDER BY 1; +SELECT * FROM mv_ivm_distinct ORDER BY 1; +ROLLBACK; + +-- support SUM(), COUNT() and AVG() aggregate functions +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg AS SELECT i, SUM(j), COUNT(i), AVG(j) FROM mv_base_a GROUP BY i; +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3,4; +INSERT INTO mv_base_a VALUES(2,100); +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3,4; +UPDATE mv_base_a SET j = 200 WHERE (i,j) = (2,100); +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3,4; +DELETE FROM mv_base_a WHERE (i,j) = (2,200); +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3,4; +ROLLBACK; + +-- support COUNT(*) aggregate function +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg AS SELECT i, SUM(j), COUNT(*) FROM mv_base_a GROUP BY i; +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3; +INSERT INTO mv_base_a VALUES(2,100); +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3; +ROLLBACK; + +-- TRUNCATE a base table in aggregate views +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg AS SELECT i, SUM(j), COUNT(*) FROM mv_base_a GROUP BY i; +TRUNCATE mv_base_a; +SELECT sum, count FROM mv_ivm_agg; +SELECT i, SUM(j), COUNT(*) FROM mv_base_a GROUP BY i; +ROLLBACK; + +-- support aggregate functions without GROUP clause +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_group AS SELECT SUM(j), COUNT(j), AVG(j) FROM mv_base_a; +SELECT * FROM mv_ivm_group ORDER BY 1; +INSERT INTO mv_base_a VALUES(6,60); +SELECT * FROM mv_ivm_group ORDER BY 1; +DELETE FROM mv_base_a; +SELECT * FROM mv_ivm_group ORDER BY 1; +ROLLBACK; + +-- TRUNCATE a base table in aggregate views without GROUP clause +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_group AS SELECT SUM(j), COUNT(j), AVG(j) FROM mv_base_a; +TRUNCATE mv_base_a; +SELECT sum, count, avg FROM mv_ivm_group; +SELECT SUM(j), COUNT(j), AVG(j) FROM mv_base_a; +ROLLBACK; + +-- resolved issue: When use AVG() function and values is indivisible, result of AVG() is incorrect. +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_avg_bug AS SELECT i, SUM(j), COUNT(j), AVG(j) FROM mv_base_A GROUP BY i; +SELECT * FROM mv_ivm_avg_bug ORDER BY 1,2,3; +INSERT INTO mv_base_a VALUES + (1,0), + (1,0), + (2,30), + (2,30); +SELECT * FROM mv_ivm_avg_bug ORDER BY 1,2,3; +DELETE FROM mv_base_a WHERE (i,j) = (1,0); +DELETE FROM mv_base_a WHERE (i,j) = (2,30); +SELECT * FROM mv_ivm_avg_bug ORDER BY 1,2,3; +ROLLBACK; + +-- support MIN(), MAX() aggregate functions +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_min_max AS SELECT i, MIN(j), MAX(j) FROM mv_base_a GROUP BY i; +SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3; +INSERT INTO mv_base_a VALUES + (1,11), (1,12), + (2,21), (2,22), + (3,31), (3,32), + (4,41), (4,42), + (5,51), (5,52); +SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3; +DELETE FROM mv_base_a WHERE (i,j) IN ((1,10), (2,21), (3,32)); +SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3; +ROLLBACK; + +-- support MIN(), MAX() aggregate functions without GROUP clause +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_min_max AS SELECT MIN(j), MAX(j) FROM mv_base_a; +SELECT * FROM mv_ivm_min_max; +INSERT INTO mv_base_a VALUES + (0,0), (6,60), (7,70); +SELECT * FROM mv_ivm_min_max; +DELETE FROM mv_base_a WHERE (i,j) IN ((0,0), (7,70)); +SELECT * FROM mv_ivm_min_max; +DELETE FROM mv_base_a; +SELECT * FROM mv_ivm_min_max; +ROLLBACK; + +-- Test MIN/MAX after search_path change +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_min AS SELECT MIN(j) FROM mv_base_a; +SELECT * FROM mv_ivm_min; + +CREATE SCHEMA myschema; +GRANT ALL ON SCHEMA myschema TO public; +CREATE TABLE myschema.mv_base_a (j int); +INSERT INTO myschema.mv_base_a VALUES (1); + +DELETE FROM mv_base_a WHERE (i,j) = (1,10); +SELECT * FROM mv_ivm_min; + +SET search_path TO myschema,public,pg_catalog; +DELETE FROM public.mv_base_a WHERE (i,j) = (2,20); +SELECT * FROM mv_ivm_min; +ROLLBACK; + +-- aggregate views with column names specified +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg(a) AS SELECT i, SUM(j) FROM mv_base_a GROUP BY i; +INSERT INTO mv_base_a VALUES (1,100), (2,200), (3,300); +UPDATE mv_base_a SET j = 2000 WHERE (i,j) = (2,20); +DELETE FROM mv_base_a WHERE (i,j) = (3,30); +SELECT * FROM mv_ivm_agg ORDER BY 1,2; +ROLLBACK; +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg(a,b) AS SELECT i, SUM(j) FROM mv_base_a GROUP BY i; +INSERT INTO mv_base_a VALUES (1,100), (2,200), (3,300); +UPDATE mv_base_a SET j = 2000 WHERE (i,j) = (2,20); +DELETE FROM mv_base_a WHERE (i,j) = (3,30); +SELECT * FROM mv_ivm_agg ORDER BY 1,2; +ROLLBACK; +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg(a,b,c) AS SELECT i, SUM(j) FROM mv_base_a GROUP BY i; +ROLLBACK; + +-- support self join view and multiple change on the same table +BEGIN; +CREATE TABLE base_t (i int, v int); +INSERT INTO base_t VALUES (1, 10), (2, 20), (3, 30); +CREATE INCREMENTAL MATERIALIZED VIEW mv_self(v1, v2) AS + SELECT t1.v, t2.v FROM base_t AS t1 JOIN base_t AS t2 ON t1.i = t2.i; +SELECT * FROM mv_self ORDER BY v1; +INSERT INTO base_t VALUES (4,40); +DELETE FROM base_t WHERE i = 1; +UPDATE base_t SET v = v*10 WHERE i=2; +SELECT * FROM mv_self ORDER BY v1; +WITH + ins_t1 AS (INSERT INTO base_t VALUES (5,50) RETURNING 1), + ins_t2 AS (INSERT INTO base_t VALUES (6,60) RETURNING 1), + upd_t AS (UPDATE base_t SET v = v + 100 RETURNING 1), + dlt_t AS (DELETE FROM base_t WHERE i IN (4,5) RETURNING 1) +SELECT NULL; +SELECT * FROM mv_self ORDER BY v1; + +--- with sub-transactions +SAVEPOINT p1; +INSERT INTO base_t VALUES (7,70); +RELEASE SAVEPOINT p1; +INSERT INTO base_t VALUES (7,77); +SELECT * FROM mv_self ORDER BY v1, v2; + +ROLLBACK; + +-- support simultaneous table changes +BEGIN; +CREATE TABLE base_r (i int, v int); +CREATE TABLE base_s (i int, v int); +INSERT INTO base_r VALUES (1, 10), (2, 20), (3, 30); +INSERT INTO base_s VALUES (1, 100), (2, 200), (3, 300); +CREATE INCREMENTAL MATERIALIZED VIEW mv(v1, v2) AS + SELECT r.v, s.v FROM base_r AS r JOIN base_s AS s USING(i); +SELECT * FROM mv ORDER BY v1; +WITH + ins_r AS (INSERT INTO base_r VALUES (1,11) RETURNING 1), + ins_r2 AS (INSERT INTO base_r VALUES (3,33) RETURNING 1), + ins_s AS (INSERT INTO base_s VALUES (2,222) RETURNING 1), + upd_r AS (UPDATE base_r SET v = v + 1000 WHERE i = 2 RETURNING 1), + dlt_s AS (DELETE FROM base_s WHERE i = 3 RETURNING 1) +SELECT NULL; +SELECT * FROM mv ORDER BY v1; +ROLLBACK; + +-- support foreign reference constraints +BEGIN; +CREATE TABLE ri1 (i int PRIMARY KEY); +CREATE TABLE ri2 (i int PRIMARY KEY REFERENCES ri1(i) ON UPDATE CASCADE ON DELETE CASCADE, v int); +INSERT INTO ri1 VALUES (1),(2),(3); +INSERT INTO ri2 VALUES (1),(2),(3); +CREATE INCREMENTAL MATERIALIZED VIEW mv_ri(i1, i2) AS + SELECT ri1.i, ri2.i FROM ri1 JOIN ri2 USING(i); +SELECT * FROM mv_ri ORDER BY i1; +UPDATE ri1 SET i=10 where i=1; +DELETE FROM ri1 WHERE i=2; +SELECT * FROM mv_ri ORDER BY i2; +ROLLBACK; + +-- views including NULL +BEGIN; +CREATE TABLE base_t (i int, v int); +INSERT INTO base_t VALUES (1,10),(2, NULL); +CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT * FROM base_t; +SELECT * FROM mv ORDER BY i; +UPDATE base_t SET v = 20 WHERE i = 2; +SELECT * FROM mv ORDER BY i; +ROLLBACK; + +BEGIN; +CREATE TABLE base_t (i int); +CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT * FROM base_t; +SELECT * FROM mv ORDER BY i; +INSERT INTO base_t VALUES (1),(NULL); +SELECT * FROM mv ORDER BY i; +ROLLBACK; + +BEGIN; +CREATE TABLE base_t (i int, v int); +INSERT INTO base_t VALUES (NULL, 1), (NULL, 2), (1, 10), (1, 20); +CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT i, sum(v) FROM base_t GROUP BY i; +SELECT * FROM mv ORDER BY i; +UPDATE base_t SET v = v * 10; +SELECT * FROM mv ORDER BY i; +ROLLBACK; + +BEGIN; +CREATE TABLE base_t (i int, v int); +INSERT INTO base_t VALUES (NULL, 1), (NULL, 2), (NULL, 3), (NULL, 4), (NULL, 5); +CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT i, min(v), max(v) FROM base_t GROUP BY i; +SELECT * FROM mv ORDER BY i; +DELETE FROM base_t WHERE v = 1; +SELECT * FROM mv ORDER BY i; +DELETE FROM base_t WHERE v = 3; +SELECT * FROM mv ORDER BY i; +DELETE FROM base_t WHERE v = 5; +SELECT * FROM mv ORDER BY i; +ROLLBACK; + +-- IMMV containing user defined type +BEGIN; + +CREATE TYPE mytype; +CREATE FUNCTION mytype_in(cstring) + RETURNS mytype AS 'int4in' + LANGUAGE INTERNAL STRICT IMMUTABLE; +CREATE FUNCTION mytype_out(mytype) + RETURNS cstring AS 'int4out' + LANGUAGE INTERNAL STRICT IMMUTABLE; +CREATE TYPE mytype ( + LIKE = int4, + INPUT = mytype_in, + OUTPUT = mytype_out +); + +CREATE FUNCTION mytype_eq(mytype, mytype) + RETURNS bool AS 'int4eq' + LANGUAGE INTERNAL STRICT IMMUTABLE; +CREATE FUNCTION mytype_lt(mytype, mytype) + RETURNS bool AS 'int4lt' + LANGUAGE INTERNAL STRICT IMMUTABLE; +CREATE FUNCTION mytype_cmp(mytype, mytype) + RETURNS integer AS 'btint4cmp' + LANGUAGE INTERNAL STRICT IMMUTABLE; + +CREATE OPERATOR = ( + leftarg = mytype, rightarg = mytype, + procedure = mytype_eq); +CREATE OPERATOR < ( + leftarg = mytype, rightarg = mytype, + procedure = mytype_lt); + +CREATE OPERATOR CLASS mytype_ops + DEFAULT FOR TYPE mytype USING btree AS + OPERATOR 1 <, + OPERATOR 3 = , + FUNCTION 1 mytype_cmp(mytype,mytype); + +CREATE TABLE t_mytype (x mytype); +CREATE INCREMENTAL MATERIALIZED VIEW mv_mytype AS + SELECT * FROM t_mytype; +INSERT INTO t_mytype VALUES ('1'::mytype); +SELECT * FROM mv_mytype; + +ROLLBACK; + +-- outer join is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv(a,b) AS SELECT a.i, b.i FROM mv_base_a a LEFT JOIN mv_base_b b ON a.i=b.i; +-- CTE is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv AS + WITH b AS ( SELECT * FROM mv_base_b) SELECT a.i,a.j FROM mv_base_a a, b WHERE a.i = b.i; +-- contain system column +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm01 AS SELECT i,j,xmin FROM mv_base_a; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm02 AS SELECT i,j FROM mv_base_a WHERE xmin = '610'; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm04 AS SELECT i,j,xmin::text AS x_min FROM mv_base_a; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm06 AS SELECT i,j,xidsend(xmin) AS x_min FROM mv_base_a; +-- contain subquery +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm03 AS SELECT i,j FROM mv_base_a WHERE i IN (SELECT i FROM mv_base_b WHERE k < 103 ); +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm04 AS SELECT a.i,a.j FROM mv_base_a a, (SELECT * FROM mv_base_b) b WHERE a.i = b.i; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm05 AS SELECT i,j, (SELECT k FROM mv_base_b b WHERE a.i = b.i) FROM mv_base_a a; +-- contain ORDER BY +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm07 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) ORDER BY i,j,k; +-- contain HAVING +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm08 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) GROUP BY i,j,k HAVING SUM(i) > 5; + +-- contain view or materialized view +CREATE VIEW b_view AS SELECT i,k FROM mv_base_b; +CREATE MATERIALIZED VIEW b_mview AS SELECT i,k FROM mv_base_b; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm07 AS SELECT a.i,a.j FROM mv_base_a a,b_view b WHERE a.i = b.i; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm08 AS SELECT a.i,a.j FROM mv_base_a a,b_mview b WHERE a.i = b.i; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm09 AS SELECT a.i,a.j FROM mv_base_a a, (SELECT i, COUNT(*) FROM mv_base_b GROUP BY i) b WHERE a.i = b.i; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm10 AS SELECT a.i,a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i) OR a.i > 5; + +-- contain mutable functions +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm12 AS SELECT i,j FROM mv_base_a WHERE i = random()::int; + +-- LIMIT/OFFSET is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm13 AS SELECT i,j FROM mv_base_a LIMIT 10 OFFSET 5; + +-- DISTINCT ON is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm14 AS SELECT DISTINCT ON(i) i, j FROM mv_base_a; + +-- TABLESAMPLE clause is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm15 AS SELECT i, j FROM mv_base_a TABLESAMPLE SYSTEM(50); + +-- window functions are not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm16 AS SELECT *, cume_dist() OVER (ORDER BY i) AS rank FROM mv_base_a; + +-- aggregate function with some options is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm17 AS SELECT COUNT(*) FILTER(WHERE i < 3) FROM mv_base_a; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm18 AS SELECT COUNT(DISTINCT i) FROM mv_base_a; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm19 AS SELECT array_agg(j ORDER BY i DESC) FROM mv_base_a; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm20 AS SELECT i,SUM(j) FROM mv_base_a GROUP BY GROUPING SETS((i),()); + +-- inheritance parent is not supported +BEGIN; +CREATE TABLE parent (i int, v int); +CREATE TABLE child_a(options text) INHERITS(parent); +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm21 AS SELECT * FROM parent; +ROLLBACK; + +-- UNION statement is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm22 AS SELECT i,j FROM mv_base_a UNION ALL SELECT i,k FROM mv_base_b;; + +-- empty target list is not allowed with IVM +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm25 AS SELECT FROM mv_base_a; + +-- FOR UPDATE/SHARE is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm26 AS SELECT i,j FROM mv_base_a FOR UPDATE; + +-- tartget list cannot contain ivm column that start with '__ivm' +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm28 AS SELECT i AS "__ivm_count__" FROM mv_base_a; + +-- expressions specified in GROUP BY must appear in the target list. +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm29 AS SELECT COUNT(i) FROM mv_base_a GROUP BY i; + +-- experssions containing an aggregate is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm30 AS SELECT sum(i)*0.5 FROM mv_base_a; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm31 AS SELECT sum(i)/sum(j) FROM mv_base_a; + +-- VALUES is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_only_values1 AS values(1); + +-- views containing base tables with Row Level Security +DROP USER IF EXISTS ivm_admin; +DROP USER IF EXISTS ivm_user; +CREATE USER ivm_admin; +CREATE USER ivm_user; + +--- create a table with RLS +SET SESSION AUTHORIZATION ivm_admin; +CREATE TABLE rls_tbl(id int, data text, owner name); +INSERT INTO rls_tbl VALUES + (1,'foo','ivm_user'), + (2,'bar','postgres'); +CREATE TABLE num_tbl(id int, num text); +INSERT INTO num_tbl VALUES + (1,'one'), + (2,'two'), + (3,'three'), + (4,'four'), + (5,'five'), + (6,'six'); + +--- Users can access only their own rows +CREATE POLICY rls_tbl_policy ON rls_tbl FOR SELECT TO PUBLIC USING(owner = current_user); +ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY; +GRANT ALL on rls_tbl TO PUBLIC; +GRANT ALL on num_tbl TO PUBLIC; + +--- create a view owned by ivm_user +SET SESSION AUTHORIZATION ivm_user; + +CREATE INCREMENTAL MATERIALIZED VIEW ivm_rls AS SELECT * FROM rls_tbl; +SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3; +RESET SESSION AUTHORIZATION; + +--- inserts rows owned by different users +INSERT INTO rls_tbl VALUES + (3,'baz','ivm_user'), + (4,'qux','postgres'); +SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3; + +--- combination of diffent kinds of commands +WITH + i AS (INSERT INTO rls_tbl VALUES(5,'quux','postgres'), (6,'corge','ivm_user')), + u AS (UPDATE rls_tbl SET owner = 'postgres' WHERE id = 1), + u2 AS (UPDATE rls_tbl SET owner = 'ivm_user' WHERE id = 2) +SELECT; +SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3; + +--- +SET SESSION AUTHORIZATION ivm_user; +CREATE INCREMENTAL MATERIALIZED VIEW ivm_rls2 AS SELECT * FROM rls_tbl JOIN num_tbl USING(id); +RESET SESSION AUTHORIZATION; + +WITH + x AS (UPDATE rls_tbl SET data = data || '_2' where id in (3,4)), + y AS (UPDATE num_tbl SET num = num || '_2' where id in (3,4)) +SELECT; +SELECT * FROM ivm_rls2 ORDER BY 1,2,3; + +-- automatic index creation +CREATE TABLE base_a (i int primary key, j int); +CREATE TABLE base_b (i int primary key, j int); + +--- group by: create an index +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx1 AS SELECT i, sum(j) FROM base_a GROUP BY i; + +--- distinct: create an index +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx2 AS SELECT DISTINCT j FROM base_a; + +--- with all pkey columns: create an index +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx3(i_a, i_b) AS SELECT a.i, b.i FROM base_a a, base_b b; + +--- missing some pkey columns: no index +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx4 AS SELECT j FROM base_a; +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx5 AS SELECT a.i, b.j FROM base_a a, base_b b; + +-- cleanup + +DROP TABLE rls_tbl CASCADE; +DROP TABLE num_tbl CASCADE; +DROP USER ivm_user; +DROP USER ivm_admin; + +DROP TABLE mv_base_b CASCADE; +DROP TABLE mv_base_a CASCADE; -- 2.25.1 --Multipart=_Mon__4_Mar_2024_11_58_46_+0900_UaponF/qQhQrVCFt Content-Type: text/x-diff; name="v30-0011-Add-documentations-about-Incremental-View-Mainte.patch" Content-Disposition: attachment; filename="v30-0011-Add-documentations-about-Incremental-View-Mainte.patch" Content-Transfer-Encoding: 7bit ^ permalink raw reply [nested|flat] 51+ messages in thread
end of thread, other threads:[~2021-03-10 02:11 UTC | newest] Thread overview: 51+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2016-09-08 16:00 [PATCH v2] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-25 14:35 [PATCH v4] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-25 14:35 [PATCH v4] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-25 14:35 [PATCH v4] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-25 14:35 [PATCH v4] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-25 14:35 [PATCH v4] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-25 14:35 [PATCH v4] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-25 14:35 [PATCH v4] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-25 14:35 [PATCH v4] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-25 14:35 [PATCH v4] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-25 14:35 [PATCH v4] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-25 14:35 [PATCH v4] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-25 14:35 [PATCH v4] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-25 14:35 [PATCH v4] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-25 14:35 [PATCH v4] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-25 14:35 [PATCH v4] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-25 14:35 [PATCH v4] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-28 04:52 [PATCH v5] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-28 04:52 [PATCH v5] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-28 04:52 [PATCH v5] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-28 04:52 [PATCH v5] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-28 04:52 [PATCH v5] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-28 04:52 [PATCH v5] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-28 04:52 [PATCH v5] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-28 04:52 [PATCH v5] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-28 04:52 [PATCH v5] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-28 04:52 [PATCH v5] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-28 04:52 [PATCH v5] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-28 04:52 [PATCH v5] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-28 04:52 [PATCH v5] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-28 04:52 [PATCH v5] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-28 04:52 [PATCH v5] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2017-01-28 04:52 [PATCH v5] Add CREATE SEQUENCE AS <data type> clause Peter Eisentraut <[email protected]> 2021-03-10 02:11 [PATCH v30 10/11] Add regression tests for Incremental View Maintenance Takuma Hoshiai <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox