diff --git a/CHANGELOG.md b/CHANGELOG.md index f983109aef..5a5f09e506 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,9 @@ ## Improvements +* [CORE-5430](http://tracker.firebirdsql.org/browse/CORE-5430): Support for INCREMENT option in identity columns + Contributor(s): Adriano dos Santos Fernandes + * [CORE-5119](http://tracker.firebirdsql.org/browse/CORE-5119): Support autocommit mode in SET TRANSACTION statement Contributor(s): Dmitry Yemanov diff --git a/doc/sql.extensions/README.identity_columns.txt b/doc/sql.extensions/README.identity_columns.txt index 2da196909e..acf47a612b 100644 --- a/doc/sql.extensions/README.identity_columns.txt +++ b/doc/sql.extensions/README.identity_columns.txt @@ -11,10 +11,15 @@ Description: Syntax: ::= - GENERATED BY DEFAULT AS IDENTITY [ (START WITH ) ] + GENERATED BY DEFAULT AS IDENTITY [ ( ... ) ] + + ::= + START WITH | + INCREMENT [ BY ] ::= - RESTART [ WITH ] + RESTART [ WITH ] | + SET INCREMENT [ BY ] Syntax rules: - The type of an identity column must be an exact number type with zero scale. That includes: @@ -25,6 +30,7 @@ Notes: - You cannot alter a identity column to normal column and vice versa. - Identity columns are implicitly NOT NULL. - Identity columns don't enforce uniqueness automatically. Use UNIQUE or PRIMARY key for that. + - Increment value cannot be 0. Implementation: Two columns have been inserted in RDB$RELATION_FIELDS: RDB$GENERATOR_NAME and RDB$IDENTITY_TYPE. diff --git a/lang_helpers/gds_codes.ftn b/lang_helpers/gds_codes.ftn index 7631637f1d..b7419617f1 100644 --- a/lang_helpers/gds_codes.ftn +++ b/lang_helpers/gds_codes.ftn @@ -1948,6 +1948,8 @@ C -- PARAMETER (GDS__dyn_cant_use_in_foreignkey = 336068897) INTEGER*4 GDS__dyn_defvaldecl_package_func PARAMETER (GDS__dyn_defvaldecl_package_func = 336068898) + INTEGER*4 GDS__dyn_cant_use_zero_inc_ident + PARAMETER (GDS__dyn_cant_use_zero_inc_ident = 336068904) INTEGER*4 GDS__gbak_unknown_switch PARAMETER (GDS__gbak_unknown_switch = 336330753) INTEGER*4 GDS__gbak_page_size_missing diff --git a/lang_helpers/gds_codes.pas b/lang_helpers/gds_codes.pas index b480146947..f428092767 100644 --- a/lang_helpers/gds_codes.pas +++ b/lang_helpers/gds_codes.pas @@ -1943,6 +1943,8 @@ const gds_dyn_cant_use_in_foreignkey = 336068897; isc_dyn_defvaldecl_package_func = 336068898; gds_dyn_defvaldecl_package_func = 336068898; + isc_dyn_cant_use_zero_inc_ident = 336068904; + gds_dyn_cant_use_zero_inc_ident = 336068904; isc_gbak_unknown_switch = 336330753; gds_gbak_unknown_switch = 336330753; isc_gbak_page_size_missing = 336330754; diff --git a/src/common/classes/Nullable.h b/src/common/classes/Nullable.h index 8f8bf42969..54352ec980 100644 --- a/src/common/classes/Nullable.h +++ b/src/common/classes/Nullable.h @@ -60,6 +60,11 @@ public: return nullable; } + T orElse(T elseValue) const + { + return specified ? value : elseValue; + } + bool operator ==(const BaseNullable& o) const { return (!specified && !o.specified) || (specified == o.specified && value == o.value); diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 6a8fdcc6fb..7b1149c077 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -6279,6 +6279,13 @@ void RelationNode::defineField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch if (clause->identity) { + if (clause->identity->increment.orElse(1) == 0) + { + status_exception::raise(Arg::Gds(isc_dyn_cant_use_zero_inc_ident) << + Arg::Str(field->fld_name) << + Arg::Str(name)); + } + dsc desc; MET_get_domain(tdbb, *tdbb->getDefaultPool(), fieldDefinition.fieldSource, &desc, NULL); @@ -6291,7 +6298,9 @@ void RelationNode::defineField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch DYN_UTIL_generate_generator_name(tdbb, fieldDefinition.identitySequence); CreateAlterSequenceNode::store(tdbb, transaction, fieldDefinition.identitySequence, - fb_sysflag_identity_generator, clause->identityStart, 1); + fb_sysflag_identity_generator, + clause->identity->start.orElse(0), + clause->identity->increment.orElse(1)); } BlrDebugWriter::BlrData defaultValue; @@ -7838,7 +7847,7 @@ void AlterRelationNode::modifyField(thread_db* tdbb, DsqlCompilerScratch* dsqlSc } END_MODIFY } - else if (clause->identityRestart) + else if (clause->identityRestart || clause->identityIncrement.specified) { bool found = false; AutoRequest request2; @@ -7849,11 +7858,29 @@ void AlterRelationNode::modifyField(thread_db* tdbb, DsqlCompilerScratch* dsqlSc { const SLONG id = GEN.RDB$GENERATOR_ID; const MetaName genName(RFR.RDB$GENERATOR_NAME); - const SINT64 val = clause->identityRestartValue.specified ? - clause->identityRestartValue.value : - (!GEN.RDB$INITIAL_VALUE.NULL ? GEN.RDB$INITIAL_VALUE : 0); - transaction->getGenIdCache()->put(id, val); + if (clause->identityRestart) + { + const SINT64 val = clause->identityRestartValue.specified ? + clause->identityRestartValue.value : + (!GEN.RDB$INITIAL_VALUE.NULL ? GEN.RDB$INITIAL_VALUE : 0); + + transaction->getGenIdCache()->put(id, val); + } + else if (clause->identityIncrement.specified) + { + if (clause->identityIncrement.value == 0) + { + status_exception::raise(Arg::Gds(isc_dyn_cant_use_zero_inc_ident) << + Arg::Str(field->fld_name) << + Arg::Str(name)); + } + + MET_update_generator_increment(tdbb, id, clause->identityIncrement.value); + } + else + fb_assert(false); + dsc desc; desc.makeText((USHORT) genName.length(), ttype_metadata, (UCHAR*) genName.c_str()); diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index 1af1e4471f..8c688f2e59 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -1304,6 +1304,16 @@ public: NestConst check; }; + struct IdentityOptions + { + IdentityOptions(MemoryPool&) + { + } + + Nullable start; + Nullable increment; + }; + struct AddColumnClause : public Clause { explicit AddColumnClause(MemoryPool& p) @@ -1313,8 +1323,7 @@ public: constraints(p), collate(p), computed(NULL), - identity(false), - identityStart(0), + identity(NULL), notNullSpecified(false) { } @@ -1324,8 +1333,7 @@ public: Firebird::ObjectsArray constraints; Firebird::MetaName collate; NestConst computed; - bool identity; - SINT64 identityStart; + NestConst identity; bool notNullSpecified; }; @@ -1385,6 +1393,7 @@ public: bool dropDefault; bool identityRestart; Nullable identityRestartValue; + Nullable identityIncrement; NestConst computed; }; diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h index a557aa54da..a9d475ac96 100644 --- a/src/dsql/ExprNodes.h +++ b/src/dsql/ExprNodes.h @@ -698,9 +698,9 @@ public: const bool dialect1; GeneratorItem generator; NestConst arg; + SLONG step; private: - SLONG step; bool sysGen; const bool implicit; const bool identity; diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 9d4b05db44..168f417e4e 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -6839,17 +6839,13 @@ void StoreNode::makeDefaults(thread_db* tdbb, CompilerScratch* csb) if (generatorName.hasData()) { - // Make a gen_id(, 1) expression. + // Make a (next value for ) expression. - LiteralNode* literal = FB_NEW_POOL(csb->csb_pool) LiteralNode(csb->csb_pool); - SLONG* increment = FB_NEW_POOL(csb->csb_pool) SLONG(1); - literal->litDesc.makeLong(0, increment); - - GenIdNode* const genNode = FB_NEW_POOL(csb->csb_pool) - GenIdNode(csb->csb_pool, (csb->blrVersion == 4), generatorName, literal, false, true); + GenIdNode* const genNode = FB_NEW_POOL(csb->csb_pool) GenIdNode( + csb->csb_pool, (csb->blrVersion == 4), generatorName, NULL, true, true); bool sysGen = false; - if (!MET_load_generator(tdbb, genNode->generator, &sysGen)) + if (!MET_load_generator(tdbb, genNode->generator, &sysGen, &genNode->step)) PAR_error(csb, Arg::Gds(isc_gennotdef) << Arg::Str(generatorName)); if (sysGen) diff --git a/src/dsql/parse.y b/src/dsql/parse.y index c3582b3580..1bed23b666 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -722,6 +722,7 @@ using namespace Firebird; Jrd::RelationNode::AddColumnClause* addColumnClause; Jrd::RelationNode::RefActionClause* refActionClause; Jrd::RelationNode::IndexConstraintClause* indexConstraintClause; + Jrd::RelationNode::IdentityOptions* identityOptions; Jrd::CreateRelationNode* createRelationNode; Jrd::CreateAlterViewNode* createAlterViewNode; Jrd::CreateIndexNode* createIndexNode; @@ -2148,8 +2149,7 @@ column_def($relationNode) newNode(); clause->field = $2; clause->field->fld_name = *$1; - clause->identity = true; - clause->identityStart = $3; + clause->identity = $3; $relationNode->clauses.add(clause); } column_constraint_clause(NOTRIAL($4)) collate_clause @@ -2177,15 +2177,32 @@ column_def($relationNode) } ; -%type identity_clause +%type identity_clause identity_clause - : GENERATED BY DEFAULT AS IDENTITY identity_clause_options { $$ = $6; } + : GENERATED BY DEFAULT AS IDENTITY + { $$ = newNode(); } + identity_clause_options_opt($6) + { $$ = $6; } ; -%type identity_clause_options -identity_clause_options - : /* nothing */ { $$ = 0; } - | '(' START WITH sequence_value ')' { $$ = $4; } +%type identity_clause_options_opt() +identity_clause_options_opt($identityOptions) + : // nothing + | '(' identity_clause_options($identityOptions) ')' + ; + +%type identity_clause_options() +identity_clause_options($identityOptions) + : identity_clause_options identity_clause_option($identityOptions) + | identity_clause_option($identityOptions) + ; + +%type identity_clause_option() +identity_clause_option($identityOptions) + : START WITH sequence_value + { setClause($identityOptions->start, "START WITH", $3); } + | INCREMENT by_noise signed_long_integer + { setClause($identityOptions->increment, "INCREMENT BY", $3); } ; // value does allow parens around it, but there is a problem getting the source text. @@ -3919,6 +3936,14 @@ alter_op($relationNode) clause->identityRestartValue = $4; $relationNode->clauses.add(clause); } + | col_opt symbol_column_name SET INCREMENT by_noise signed_long_integer + { + RelationNode::AlterColTypeClause* clause = newNode(); + clause->field = newNode(); + clause->field->fld_name = *$2; + clause->identityIncrement = $6; + $relationNode->clauses.add(clause); + } | ALTER SQL SECURITY DEFINER { RelationNode::AlterSqlSecurityClause* clause = diff --git a/src/include/gen/codetext.h b/src/include/gen/codetext.h index e0e29ea127..40409fd74d 100644 --- a/src/include/gen/codetext.h +++ b/src/include/gen/codetext.h @@ -970,6 +970,7 @@ static const struct { {"dyn_cant_use_zero_increment", 336068896}, {"dyn_cant_use_in_foreignkey", 336068897}, {"dyn_defvaldecl_package_func", 336068898}, + {"dyn_cant_use_zero_inc_ident", 336068904}, {"gbak_unknown_switch", 336330753}, {"gbak_page_size_missing", 336330754}, {"gbak_page_size_toobig", 336330755}, diff --git a/src/include/gen/iberror.h b/src/include/gen/iberror.h index d5bb90172e..6ecd4913a3 100644 --- a/src/include/gen/iberror.h +++ b/src/include/gen/iberror.h @@ -1004,6 +1004,7 @@ const ISC_STATUS isc_dyn_cant_modify_sysobj = 336068895L; const ISC_STATUS isc_dyn_cant_use_zero_increment = 336068896L; const ISC_STATUS isc_dyn_cant_use_in_foreignkey = 336068897L; const ISC_STATUS isc_dyn_defvaldecl_package_func = 336068898L; +const ISC_STATUS isc_dyn_cant_use_zero_inc_ident = 336068904L; const ISC_STATUS isc_gbak_unknown_switch = 336330753L; const ISC_STATUS isc_gbak_page_size_missing = 336330754L; const ISC_STATUS isc_gbak_page_size_toobig = 336330755L; @@ -1334,7 +1335,7 @@ const ISC_STATUS isc_trace_switch_user_only = 337182757L; const ISC_STATUS isc_trace_switch_param_miss = 337182758L; const ISC_STATUS isc_trace_param_act_notcompat = 337182759L; const ISC_STATUS isc_trace_mandatory_switch_miss = 337182760L; -const ISC_STATUS isc_err_max = 1278; +const ISC_STATUS isc_err_max = 1279; #else /* c definitions */ @@ -2308,6 +2309,7 @@ const ISC_STATUS isc_err_max = 1278; #define isc_dyn_cant_use_zero_increment 336068896L #define isc_dyn_cant_use_in_foreignkey 336068897L #define isc_dyn_defvaldecl_package_func 336068898L +#define isc_dyn_cant_use_zero_inc_ident 336068904L #define isc_gbak_unknown_switch 336330753L #define isc_gbak_page_size_missing 336330754L #define isc_gbak_page_size_toobig 336330755L @@ -2638,7 +2640,7 @@ const ISC_STATUS isc_err_max = 1278; #define isc_trace_switch_param_miss 337182758L #define isc_trace_param_act_notcompat 337182759L #define isc_trace_mandatory_switch_miss 337182760L -#define isc_err_max 1278 +#define isc_err_max 1279 #endif diff --git a/src/include/gen/msgs.h b/src/include/gen/msgs.h index ceef2f5836..d11466ac8c 100644 --- a/src/include/gen/msgs.h +++ b/src/include/gen/msgs.h @@ -973,6 +973,7 @@ Data source : @4"}, /* eds_statement */ {336068896, "INCREMENT BY 0 is an illegal option for sequence @1"}, /* dyn_cant_use_zero_increment */ {336068897, "Can't use @1 in FOREIGN KEY constraint"}, /* dyn_cant_use_in_foreignkey */ {336068898, "Default values for parameters are allowed only in declaration of packaged function @1.@2"}, /* dyn_defvaldecl_package_func */ + {336068904, "INCREMENT BY 0 is an illegal option for identity column @1 of table @2"}, /* dyn_cant_use_zero_inc_ident */ {336330753, "found unknown switch"}, /* gbak_unknown_switch */ {336330754, "page size parameter missing"}, /* gbak_page_size_missing */ {336330755, "Page size specified (@1) greater than limit (32768 bytes)"}, /* gbak_page_size_toobig */ diff --git a/src/include/gen/sql_code.h b/src/include/gen/sql_code.h index 0312e0baf9..ba0b149e91 100644 --- a/src/include/gen/sql_code.h +++ b/src/include/gen/sql_code.h @@ -969,6 +969,7 @@ static const struct { {336068896, -901}, /* 288 dyn_cant_use_zero_increment */ {336068897, -901}, /* 289 dyn_cant_use_in_foreignkey */ {336068898, -901}, /* 290 dyn_defvaldecl_package_func */ + {336068904, -901}, /* 296 dyn_cant_use_zero_inc_ident */ {336330753, -901}, /* 1 gbak_unknown_switch */ {336330754, -901}, /* 2 gbak_page_size_missing */ {336330755, -901}, /* 3 gbak_page_size_toobig */ diff --git a/src/include/gen/sql_state.h b/src/include/gen/sql_state.h index 43930d4119..4436c83cdf 100644 --- a/src/include/gen/sql_state.h +++ b/src/include/gen/sql_state.h @@ -969,6 +969,7 @@ static const struct { {336068896, "42000"}, // 288 dyn_cant_use_zero_increment {336068897, "42000"}, // 289 dyn_cant_use_in_foreignkey {336068898, "42000"}, // 290 dyn_defvaldecl_package_func + {336068904, "42000"}, // 296 dyn_cant_use_zero_inc_ident {336330753, "00000"}, // 1 gbak_unknown_switch {336330754, "00000"}, // 2 gbak_page_size_missing {336330755, "00000"}, // 3 gbak_page_size_toobig diff --git a/src/msgs/facilities2.sql b/src/msgs/facilities2.sql index 4c324e99bb..45406dd5d2 100644 --- a/src/msgs/facilities2.sql +++ b/src/msgs/facilities2.sql @@ -6,7 +6,7 @@ set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUM ('2015-01-07 18:01:51', 'GFIX', 3, 134) ('1996-11-07 13:39:40', 'GPRE', 4, 1) ('2016-02-23 00:00:00', 'DSQL', 7, 40) -('2016-05-30 17:56:47', 'DYN', 8, 296) +('2016-12-27 12:30:00', 'DYN', 8, 297) ('1996-11-07 13:39:40', 'INSTALL', 10, 1) ('1996-11-07 13:38:41', 'TEST', 11, 4) ('2015-07-23 14:20:00', 'GBAK', 12, 370) diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index 7551bed3fc..e5a6051a1e 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -1992,6 +1992,7 @@ COMMIT WORK; (NULL, 'CreateAlterRoleNode::execute', 'DdlNodes.epp', NULL, 8, 293, NULL, 'DROP SYSTEM PRIVILEGES should not be used in CREATE ROLE operator', NULL, NULL); (NULL, 'CreateAlterRoleNode::execute', 'DdlNodes.epp', NULL, 8, 294, NULL, 'Access to SYSTEM PRIVILEGES in ROLES denied to @1', NULL, NULL); (NULL, 'grant/revoke', 'DdlNode.epp', NULL, 8, 295, NULL, 'Only @1, DB owner @2 or user with privilege USE_GRANTED_BY_CLAUSE can use GRANTED BY clause', NULL, NULL); +('dyn_cant_use_zero_inc_ident', NULL, 'DdlNodes.epp', NULL, 8, 296, NULL, 'INCREMENT BY 0 is an illegal option for identity column @1 of table @2', NULL, NULL); COMMIT WORK; -- TEST (NULL, 'main', 'test.c', NULL, 11, 0, NULL, 'This is a modified text message', NULL, NULL); diff --git a/src/msgs/system_errors2.sql b/src/msgs/system_errors2.sql index 9553c7a4c4..0be14bbbf8 100644 --- a/src/msgs/system_errors2.sql +++ b/src/msgs/system_errors2.sql @@ -958,6 +958,7 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA (-901, '42', '000', 8, 288, 'dyn_cant_use_zero_increment', NULL, NULL) (-901, '42', '000', 8, 289, 'dyn_cant_use_in_foreignkey', NULL, NULL) (-901, '42', '000', 8, 290, 'dyn_defvaldecl_package_func', NULL, NULL) +(-901, '42', '000', 8, 296, 'dyn_cant_use_zero_inc_ident', NULL, NULL) -- GBAK (-901, '00', '000', 12, 1, 'gbak_unknown_switch', NULL, NULL) (-901, '00', '000', 12, 2, 'gbak_page_size_missing', NULL, NULL)