8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 20:43:02 +01:00

Feature CORE-5463 - Support GENERATED ALWAYS identity columns and OVERRIDE clause.

I didn't verified why the error messages are being truncated. It seems idiotic if
the engine, library or ISQL does not accept these not-so-detailed messages.
This commit is contained in:
Adriano dos Santos Fernandes 2017-02-24 23:03:04 -03:00
parent 0b80f7dbba
commit 3ca6fc140d
29 changed files with 349 additions and 104 deletions

View File

@ -11,16 +11,20 @@ Description:
Syntax:
<column definition> ::=
<name> <type> GENERATED BY DEFAULT AS IDENTITY [ ( <identity column option>... ) ] <constraints>
<name> <type> GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <identity column option>... ) ] <constraints>
<identity column option> ::=
START WITH <value> |
INCREMENT [ BY ] <value>
<alter column definition> ::=
<name> <set identity column generation clause> [ <alter identity column option>... ] |
<name> <alter identity column option>... |
<name> DROP IDENTITY
<set identity column generation clause> ::=
SET GENERATED { ALWAYS | BY DEFAULT }
<alter identity column option> ::=
RESTART [ WITH <value> ] |
SET INCREMENT [ BY ] <value>
@ -39,10 +43,8 @@ Notes:
Implementation:
Two columns have been inserted in RDB$RELATION_FIELDS: RDB$GENERATOR_NAME and RDB$IDENTITY_TYPE.
RDB$GENERATOR_NAME stores the automatically created generator for the column. In RDB$GENERATORS,
the value of RDB$SYSTEM_FLAG of that generator will be 6. RDB$IDENTITY_TYPE will currently
always store the value 1 (by default) for identity columns and NULL for non-identity columns.
In the future this column will store the value 0, too (for ALWAYS) when Firebird support this type
of identity column.
the value of RDB$SYSTEM_FLAG of that generator will be 6. RDB$IDENTITY_TYPE stores the value
0 for GENERATED ALWAYS, 1 for GENERATED BY DEFAULT, and NULL for non-identity columns.
Example:
@ -84,3 +86,21 @@ alter table objects
alter table objects
alter id drop identity;
---------------
Override Clause
---------------
BY DEFAULT identity columns can be overriden in INSERT statements (INSERT, UPDATE OR INSERT, MERGE ... WHEN NOT MATCHED)
just specifying the value in the values list. However, for ALWAYS identity columns that is not allowed.
To use the value passed in the INSERT statement for an ALWAYS column, you should pass OVERRIDING SYSTEM VALUE as
following:
insert into objects (id, name) overriding system value values (11, 'Laptop');
OVERRIDING also supports a subclause to be used with BY DEFAULT columns, to ignore the value passed in INSERT and use
the defined sequence:
insert into objects (id, name) overriding user value values (12, 'Laptop'); -- 12 is not used

View File

@ -1678,6 +1678,14 @@ C --
PARAMETER (GDS__att_shut_db_down = 335545132)
INTEGER*4 GDS__att_shut_engine
PARAMETER (GDS__att_shut_engine = 335545133)
INTEGER*4 GDS__overriding_without_identity
PARAMETER (GDS__overriding_without_identity = 335545134)
INTEGER*4 GDS__overriding_system_invalid
PARAMETER (GDS__overriding_system_invalid = 335545135)
INTEGER*4 GDS__overriding_user_invalid
PARAMETER (GDS__overriding_user_invalid = 335545136)
INTEGER*4 GDS__overriding_system_missing
PARAMETER (GDS__overriding_system_missing = 335545137)
INTEGER*4 GDS__gfix_db_name
PARAMETER (GDS__gfix_db_name = 335740929)
INTEGER*4 GDS__gfix_invalid_sw

View File

@ -1673,6 +1673,14 @@ const
gds_att_shut_db_down = 335545132;
isc_att_shut_engine = 335545133;
gds_att_shut_engine = 335545133;
isc_overriding_without_identity = 335545134;
gds_overriding_without_identity = 335545134;
isc_overriding_system_invalid = 335545135;
gds_overriding_system_invalid = 335545135;
isc_overriding_user_invalid = 335545136;
gds_overriding_user_invalid = 335545136;
isc_overriding_system_missing = 335545137;
gds_overriding_system_missing = 335545137;
isc_gfix_db_name = 335740929;
gds_gfix_db_name = 335740929;
isc_gfix_invalid_sw = 335740930;

View File

@ -35,7 +35,7 @@ class NullableClear
public:
static void clear(T& v)
{
v = 0;
v = T();
}
};
@ -70,6 +70,11 @@ public:
return (!specified && !o.specified) || (specified == o.specified && value == o.value);
}
bool operator ==(const T& o) const
{
return specified && value == o;
}
void operator =(const T& v)
{
this->value = v;
@ -84,26 +89,6 @@ public:
// NullableClear specializations.
template <>
class NullableClear<Firebird::string> // string especialization for NullableClear
{
public:
static void clear(Firebird::string& v)
{
v = "";
}
};
template <>
class NullableClear<Firebird::MetaName> // MetaName especialization for NullableClear
{
public:
static void clear(Firebird::MetaName& v)
{
v = "";
}
};
template <typename T>
class NullableClear<BaseNullable<T> >
{

View File

@ -5951,7 +5951,7 @@ void RelationNode::FieldDefinition::store(thread_db* tdbb, jrd_tra* transaction)
strcpy(RFR.RDB$GENERATOR_NAME, identitySequence.c_str());
RFR.RDB$IDENTITY_TYPE.NULL = FALSE;
RFR.RDB$IDENTITY_TYPE = IDENT_TYPE_BY_DEFAULT;
RFR.RDB$IDENTITY_TYPE = identityType.value;
}
if (notNullFlag.specified)
@ -6296,6 +6296,7 @@ void RelationNode::defineField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch
}
DYN_UTIL_generate_generator_name(tdbb, fieldDefinition.identitySequence);
fieldDefinition.identityType = clause->identityOptions->type;
CreateAlterSequenceNode::store(tdbb, transaction, fieldDefinition.identitySequence,
fb_sysflag_identity_generator,
@ -7858,6 +7859,13 @@ void AlterRelationNode::modifyField(thread_db* tdbb, DsqlCompilerScratch* dsqlSc
transaction->getGenIdCache()->put(id, val);
}
if (clause->identityOptions->type.specified)
{
MODIFY RFR
RFR.RDB$IDENTITY_TYPE = clause->identityOptions->type.value;
END_MODIFY
}
if (clause->identityOptions->increment.specified)
{
if (clause->identityOptions->increment.value == 0)

View File

@ -1140,6 +1140,7 @@ public:
Firebird::MetaName relationName;
Firebird::MetaName fieldSource;
Firebird::MetaName identitySequence;
Nullable<IdentityType> identityType;
Nullable<USHORT> collationId;
Nullable<bool> notNullFlag; // true = NOT NULL / false = NULL
Nullable<USHORT> position;
@ -1306,11 +1307,18 @@ public:
struct IdentityOptions
{
IdentityOptions(MemoryPool&, IdentityType aType)
: type(aType),
restart(false)
{
}
IdentityOptions(MemoryPool&)
: restart(false)
{
}
Nullable<IdentityType> type;
Nullable<SINT64> startValue;
Nullable<SLONG> increment;
bool restart; // used in ALTER

View File

@ -94,6 +94,8 @@ static void postTriggerAccess(CompilerScratch* csb, jrd_rel* ownerRelation,
ExternalAccess::exa_act operation, jrd_rel* view);
static void preModifyEraseTriggers(thread_db* tdbb, TrigVector** trigs,
StmtNode::WhichTrigger whichTrig, record_param* rpb, record_param* rec, TriggerAction op);
static void preprocessAssignments(thread_db* tdbb, CompilerScratch* csb,
StreamType stream, CompoundStmtNode* compoundNode, const Nullable<OverrideClause>* insertOverride);
static void validateExpressions(thread_db* tdbb, const Array<ValidateInfo>& validations);
} // namespace Jrd
@ -5340,6 +5342,7 @@ StmtNode* MergeNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
store->dsqlRelation = relation;
store->dsqlFields = notMatched->fields;
store->dsqlValues = notMatched->values;
store->overrideClause = notMatched->overrideClause;
bool needSavePoint; // unused
thisIf->trueAction = store = store->internalDsqlPass(dsqlScratch, false, needSavePoint)->as<StoreNode>();
@ -5881,50 +5884,7 @@ void ModifyNode::genBlr(DsqlCompilerScratch* dsqlScratch)
ModifyNode* ModifyNode::pass1(thread_db* tdbb, CompilerScratch* csb)
{
CompoundStmtNode* compoundNode = statement->as<CompoundStmtNode>();
// Remove assignments of DEFAULT to computed fields.
if (compoundNode)
{
for (size_t i = compoundNode->statements.getCount(); i--; )
{
const AssignmentNode* assign = compoundNode->statements[i]->as<AssignmentNode>();
fb_assert(assign);
if (!assign)
continue;
const ExprNode* assignFrom = assign->asgnFrom;
const FieldNode* assignToField = assign->asgnTo->as<FieldNode>();
if (assignToField && assignFrom->is<DefaultNode>())
{
jrd_rel* relation = csb->csb_rpt[newStream].csb_relation;
int fieldId = assignToField->fieldId;
while (true)
{
jrd_fld* fld;
if (assignToField->fieldStream == newStream &&
relation &&
relation->rel_fields &&
(fld = (*relation->rel_fields)[fieldId]))
{
if (fld->fld_computation)
compoundNode->statements.remove(i);
else if (relation->rel_view_rse && fld->fld_source_rel_field.first.hasData())
{
relation = MET_lookup_relation(tdbb, fld->fld_source_rel_field.first);
if ((fieldId = MET_lookup_field(tdbb, relation, fld->fld_source_rel_field.second)) >= 0)
continue;
}
}
break;
}
}
}
}
preprocessAssignments(tdbb, csb, newStream, statement->as<CompoundStmtNode>(), NULL);
pass1Modify(tdbb, csb, this);
@ -6463,6 +6423,7 @@ const StmtNode* ReceiveNode::execute(thread_db* /*tdbb*/, jrd_req* request, ExeS
static RegisterNode<StoreNode> regStoreNode(blr_store);
static RegisterNode<StoreNode> regStoreNode2(blr_store2);
static RegisterNode<StoreNode> regStoreNode3(blr_store3);
// Parse a store statement.
DmlNode* StoreNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp)
@ -6471,6 +6432,21 @@ DmlNode* StoreNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* cs
AutoSetRestore<StmtNode*> autoCurrentDMLNode(&csb->csb_currentDMLNode, node);
if (blrOp == blr_store3)
{
node->overrideClause = static_cast<OverrideClause>(csb->csb_blr_reader.getByte());
switch (node->overrideClause.value)
{
case OverrideClause::USER_VALUE:
case OverrideClause::SYSTEM_VALUE:
break;
default:
PAR_syntax_error(csb, "invalid blr_store3 override clause");
}
}
const UCHAR* blrPos = csb->csb_blr_reader.getPos();
node->relationSource = PAR_parseRecordSource(tdbb, csb)->as<RelationSourceNode>();
@ -6485,6 +6461,13 @@ DmlNode* StoreNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* cs
if (blrOp == blr_store2)
node->statement2 = PAR_parse_stmt(tdbb, csb);
else if (blrOp == blr_store3)
{
if (csb->csb_blr_reader.peekByte() == blr_null)
csb->csb_blr_reader.getByte();
else
node->statement2 = PAR_parse_stmt(tdbb, csb);
}
return node;
}
@ -6498,6 +6481,7 @@ StmtNode* StoreNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch,
dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_INSERT);
StoreNode* node = FB_NEW_POOL(getPool()) StoreNode(getPool());
node->overrideClause = overrideClause;
// Process SELECT expression, if present
@ -6606,7 +6590,21 @@ StmtNode* StoreNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch,
NestConst<ValueExprNode>* ptr2 = values->items.begin();
for (const NestConst<ValueExprNode>* end = fields.end(); ptr != end; ++ptr, ++ptr2)
{
if (*ptr2) // it's NULL for DEFAULT
// *ptr2 is NULL for DEFAULT
if (!*ptr2)
{
const FieldNode* field = (*ptr)->as<FieldNode>();
if (field && field->dsqlField)
{
*ptr2 = FB_NEW_POOL(getPool()) DefaultNode(getPool(),
relation->rel_name, field->dsqlField->fld_name);
*ptr2 = doDsqlPass(dsqlScratch, *ptr2, false);
}
}
if (*ptr2)
{
AssignmentNode* temp = FB_NEW_POOL(getPool()) AssignmentNode(getPool());
temp->asgnFrom = *ptr2;
@ -6691,12 +6689,19 @@ void StoreNode::genBlr(DsqlCompilerScratch* dsqlScratch)
{
const dsql_msg* message = dsqlGenDmlHeader(dsqlScratch, dsqlRse->as<RseNode>());
dsqlScratch->appendUChar(statement2 ? blr_store2 : blr_store);
dsqlScratch->appendUChar(overrideClause.specified ? blr_store3 : (statement2 ? blr_store2 : blr_store));
if (overrideClause.specified)
dsqlScratch->appendUChar(UCHAR(overrideClause.value));
GEN_expr(dsqlScratch, dsqlRelation);
statement->genBlr(dsqlScratch);
if (statement2)
statement2->genBlr(dsqlScratch);
else if (overrideClause.specified)
dsqlScratch->appendUChar(blr_null);
if (message)
dsqlScratch->appendUChar(blr_end);
@ -6704,6 +6709,8 @@ void StoreNode::genBlr(DsqlCompilerScratch* dsqlScratch)
StoreNode* StoreNode::pass1(thread_db* tdbb, CompilerScratch* csb)
{
preprocessAssignments(tdbb, csb, relationSource->getStream(), statement->as<CompoundStmtNode>(), &overrideClause);
if (pass1Store(tdbb, csb, this))
makeDefaults(tdbb, csb);
@ -6839,7 +6846,6 @@ void StoreNode::makeDefaults(thread_db* tdbb, CompilerScratch* csb)
}
StmtNodeStack stack;
USHORT fieldId = 0;
vec<jrd_fld*>::iterator ptr1 = vector->begin();
@ -6869,10 +6875,7 @@ void StoreNode::makeDefaults(thread_db* tdbb, CompilerScratch* csb)
fb_assert(fieldNode);
if (fieldNode && fieldNode->fieldStream == stream && fieldNode->fieldId == fieldId)
{
inList = true;
break;
}
}
}
@ -8071,6 +8074,7 @@ StmtNode* UpdateOrInsertNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
insert->dsqlFields = fields;
insert->dsqlValues = values;
insert->dsqlReturning = returning;
insert->overrideClause = overrideClause;
insert = insert->internalDsqlPass(dsqlScratch, true, needSavePoint)->as<StoreNode>();
fb_assert(insert);
@ -9324,6 +9328,99 @@ static void preModifyEraseTriggers(thread_db* tdbb, TrigVector** trigs,
tdbb->getTransaction()->tra_rpblist->PopRpb(rpb, rpblevel);
}
// 1. Remove assignments of DEFAULT to computed fields.
// 2. Remove assignments to identity column when OVERRIDING USER VALUE is specified in INSERT.
static void preprocessAssignments(thread_db* tdbb, CompilerScratch* csb,
StreamType stream, CompoundStmtNode* compoundNode, const Nullable<OverrideClause>* insertOverride)
{
if (!compoundNode)
return;
jrd_rel* relation = csb->csb_rpt[stream].csb_relation;
fb_assert(relation);
if (!relation)
return;
Nullable<IdentityType> identityType;
for (size_t i = compoundNode->statements.getCount(); i--; )
{
const AssignmentNode* assign = compoundNode->statements[i]->as<AssignmentNode>();
fb_assert(assign);
if (!assign)
continue;
const ExprNode* assignFrom = assign->asgnFrom;
const FieldNode* assignToField = assign->asgnTo->as<FieldNode>();
if (assignToField)
{
int fieldId = assignToField->fieldId;
jrd_fld* fld;
while (true)
{
if (assignToField->fieldStream == stream &&
relation->rel_fields &&
(fld = (*relation->rel_fields)[fieldId]))
{
if (insertOverride && fld->fld_identity_type.specified)
{
if (insertOverride->specified || !assignFrom->is<DefaultNode>())
identityType = fld->fld_identity_type;
if (*insertOverride == OverrideClause::USER_VALUE)
{
compoundNode->statements.remove(i);
break;
}
}
if (fld->fld_computation)
{
if (assignFrom->is<DefaultNode>())
compoundNode->statements.remove(i);
}
else if (relation->rel_view_rse && fld->fld_source_rel_field.first.hasData())
{
relation = MET_lookup_relation(tdbb, fld->fld_source_rel_field.first);
fb_assert(relation);
if (!relation)
return;
if ((fieldId = MET_lookup_field(tdbb, relation, fld->fld_source_rel_field.second)) >= 0)
continue;
}
}
break;
}
}
}
if (!insertOverride)
return;
if (insertOverride->specified)
{
if (!identityType.specified)
ERR_post(Arg::Gds(isc_overriding_without_identity) << relation->rel_name);
if (identityType == IDENT_TYPE_BY_DEFAULT && *insertOverride == OverrideClause::SYSTEM_VALUE)
ERR_post(Arg::Gds(isc_overriding_system_invalid) << relation->rel_name);
if (identityType == IDENT_TYPE_ALWAYS && *insertOverride == OverrideClause::USER_VALUE)
ERR_post(Arg::Gds(isc_overriding_user_invalid) << relation->rel_name);
}
else
{
if (identityType == IDENT_TYPE_ALWAYS)
ERR_post(Arg::Gds(isc_overriding_system_missing) << relation->rel_name);
}
}
// Execute a list of validation expressions.
static void validateExpressions(thread_db* tdbb, const Array<ValidateInfo>& validations)
{

View File

@ -112,6 +112,14 @@ struct ValidateInfo
};
enum OverrideClause : UCHAR
{
// Warning: used in BLR
USER_VALUE = 1,
SYSTEM_VALUE
};
class AssignmentNode : public TypedNode<StmtNode, StmtNode::TYPE_ASSIGNMENT>
{
public:
@ -1048,6 +1056,7 @@ public:
Firebird::Array<NestConst<FieldNode> > fields;
NestConst<ValueListNode> values;
NestConst<BoolExprNode> condition;
Nullable<OverrideClause> overrideClause;
};
explicit MergeNode(MemoryPool& pool)
@ -1267,6 +1276,7 @@ public:
NestConst<StmtNode> subStore;
Firebird::Array<ValidateInfo> validations;
NestConst<RelationSourceNode> relationSource;
Nullable<OverrideClause> overrideClause;
};
@ -1609,6 +1619,7 @@ public:
NestConst<ValueListNode> values;
Firebird::Array<NestConst<FieldNode> > matching;
NestConst<ReturningClause> returning;
Nullable<OverrideClause> overrideClause;
};

View File

@ -1 +1 @@
45 shift/reduce conflicts, 17 reduce/reduce conflicts.
46 shift/reduce conflicts, 17 reduce/reduce conflicts.

View File

@ -600,6 +600,7 @@ using namespace Firebird;
%token <metaNamePtr> MESSAGE
%token <metaNamePtr> NTILE
%token <metaNamePtr> OTHERS
%token <metaNamePtr> OVERRIDING
%token <metaNamePtr> PERCENT_RANK
%token <metaNamePtr> PRECEDING
%token <metaNamePtr> PRIVILEGE
@ -648,6 +649,7 @@ using namespace Firebird;
BaseNullable<int> nullableIntVal;
BaseNullable<bool> nullableBoolVal;
BaseNullable<Jrd::TriggerDefinition::SqlSecurity> nullableSqlSecurityVal;
BaseNullable<Jrd::OverrideClause> nullableOverrideClause;
bool boolVal;
int intVal;
unsigned uintVal;
@ -727,6 +729,7 @@ using namespace Firebird;
Jrd::RelationNode::RefActionClause* refActionClause;
Jrd::RelationNode::IndexConstraintClause* indexConstraintClause;
Jrd::RelationNode::IdentityOptions* identityOptions;
IdentityType identityType;
Jrd::CreateRelationNode* createRelationNode;
Jrd::CreateAlterViewNode* createAlterViewNode;
Jrd::CreateIndexNode* createIndexNode;
@ -2185,10 +2188,16 @@ column_def($relationNode)
%type <identityOptions> identity_clause
identity_clause
: GENERATED BY DEFAULT AS IDENTITY
{ $$ = newNode<RelationNode::IdentityOptions>(); }
identity_clause_options_opt($6)
{ $$ = $6; }
: GENERATED identity_clause_type AS IDENTITY
{ $$ = newNode<RelationNode::IdentityOptions>($2); }
identity_clause_options_opt($5)
{ $$ = $5; }
;
%type <identityType> identity_clause_type
identity_clause_type
: BY DEFAULT { $$ = IDENT_TYPE_BY_DEFAULT; }
| ALWAYS { $$ = IDENT_TYPE_ALWAYS; }
;
%type identity_clause_options_opt(<identityOptions>)
@ -3935,7 +3944,7 @@ alter_op($relationNode)
}
| col_opt symbol_column_name
{ $<identityOptions>$ = newNode<RelationNode::IdentityOptions>(); }
alter_identity_clause_options($<identityOptions>3)
alter_identity_clause_spec($<identityOptions>3)
{
RelationNode::AlterColTypeClause* clause = newNode<RelationNode::AlterColTypeClause>();
clause->field = newNode<dsql_fld>();
@ -4081,6 +4090,24 @@ alter_data_type_or_domain
}
;
%type alter_identity_clause_spec(<identityOptions>)
alter_identity_clause_spec($identityOptions)
: alter_identity_clause_generation($identityOptions) alter_identity_clause_options_opt($identityOptions)
| alter_identity_clause_options($identityOptions)
;
%type alter_identity_clause_generation(<identityOptions>)
alter_identity_clause_generation($identityOptions)
: SET GENERATED ALWAYS { $identityOptions->type = IDENT_TYPE_ALWAYS; }
| SET GENERATED BY DEFAULT { $identityOptions->type = IDENT_TYPE_BY_DEFAULT; }
;
%type alter_identity_clause_options_opt(<identityOptions>)
alter_identity_clause_options_opt($identityOptions)
: // nothing
| alter_identity_clause_options($identityOptions)
;
%type alter_identity_clause_options(<identityOptions>)
alter_identity_clause_options($identityOptions)
: alter_identity_clause_options alter_identity_clause_option($identityOptions)
@ -5942,18 +5969,20 @@ fetch_first_clause
// IBO hack: replace column_parens_opt by ins_column_parens_opt.
%type <storeNode> insert
insert
: insert_start ins_column_parens_opt(NOTRIAL(&$1->dsqlFields)) VALUES '(' value_or_default_list ')'
: insert_start ins_column_parens_opt(NOTRIAL(&$1->dsqlFields)) override_opt VALUES '(' value_or_default_list ')'
returning_clause
{
StoreNode* node = $$ = $1;
node->dsqlValues = $5;
node->dsqlReturning = $7;
node->overrideClause = $3;
node->dsqlValues = $6;
node->dsqlReturning = $8;
}
| insert_start ins_column_parens_opt(NOTRIAL(&$1->dsqlFields)) select_expr returning_clause
| insert_start ins_column_parens_opt(NOTRIAL(&$1->dsqlFields)) override_opt select_expr returning_clause
{
StoreNode* node = $$ = $1;
node->dsqlRse = $3;
node->dsqlReturning = $4;
node->overrideClause = $3;
node->dsqlRse = $4;
node->dsqlReturning = $5;
$$ = node;
}
| insert_start DEFAULT VALUES returning_clause
@ -5974,6 +6003,13 @@ insert_start
}
;
%type <nullableOverrideClause> override_opt
override_opt
: /* nothing */ { $$ = Nullable<OverrideClause>::empty(); }
| OVERRIDING USER VALUE { $$ = Nullable<OverrideClause>::val(OverrideClause::USER_VALUE); }
| OVERRIDING SYSTEM VALUE { $$ = Nullable<OverrideClause>::val(OverrideClause::SYSTEM_VALUE); }
;
%type <valueListNode> value_or_default_list
value_or_default_list
: value_or_default { $$ = newNode<ValueListNode>($1); }
@ -6042,13 +6078,17 @@ merge_update_specification($mergeMatchedClause, $relationName)
%type merge_insert_specification(<mergeNotMatchedClause>)
merge_insert_specification($mergeNotMatchedClause)
: THEN INSERT ins_column_parens_opt(NOTRIAL(&$mergeNotMatchedClause->fields))
VALUES '(' value_or_default_list ')'
{ $mergeNotMatchedClause->values = $6; }
| AND search_condition THEN INSERT ins_column_parens_opt(NOTRIAL(&$mergeNotMatchedClause->fields))
: THEN INSERT ins_column_parens_opt(NOTRIAL(&$mergeNotMatchedClause->fields)) override_opt
VALUES '(' value_or_default_list ')'
{
$mergeNotMatchedClause->values = $8;
$mergeNotMatchedClause->overrideClause = $4;
$mergeNotMatchedClause->values = $7;
}
| AND search_condition THEN INSERT ins_column_parens_opt(NOTRIAL(&$mergeNotMatchedClause->fields)) override_opt
VALUES '(' value_or_default_list ')'
{
$mergeNotMatchedClause->overrideClause = $6;
$mergeNotMatchedClause->values = $9;
$mergeNotMatchedClause->condition = $2;
}
;
@ -6138,12 +6178,13 @@ update_or_insert
UpdateOrInsertNode* node = $$ = newNode<UpdateOrInsertNode>();
node->relation = $5;
}
ins_column_parens_opt(NOTRIAL(&$6->fields)) VALUES '(' value_or_default_list ')'
ins_column_parens_opt(NOTRIAL(&$6->fields)) override_opt VALUES '(' value_or_default_list ')'
update_or_insert_matching_opt(NOTRIAL(&$6->matching)) returning_clause
{
UpdateOrInsertNode* node = $$ = $6;
node->values = $10;
node->returning = $13;
node->overrideClause = $8;
node->values = $11;
node->returning = $14;
}
;
@ -8248,6 +8289,7 @@ non_reserved_word
| MESSAGE
| NTILE
| OTHERS
| OVERRIDING
| PERCENT_RANK
| PRECEDING
| PRIVILEGE

View File

@ -835,6 +835,10 @@ static const struct {
{"att_shut_idle", 335545131},
{"att_shut_db_down", 335545132},
{"att_shut_engine", 335545133},
{"overriding_without_identity", 335545134},
{"overriding_system_invalid", 335545135},
{"overriding_user_invalid", 335545136},
{"overriding_system_missing", 335545137},
{"gfix_db_name", 335740929},
{"gfix_invalid_sw", 335740930},
{"gfix_incmp_sw", 335740932},

View File

@ -869,6 +869,10 @@ const ISC_STATUS isc_att_shut_killed = 335545130L;
const ISC_STATUS isc_att_shut_idle = 335545131L;
const ISC_STATUS isc_att_shut_db_down = 335545132L;
const ISC_STATUS isc_att_shut_engine = 335545133L;
const ISC_STATUS isc_overriding_without_identity = 335545134L;
const ISC_STATUS isc_overriding_system_invalid = 335545135L;
const ISC_STATUS isc_overriding_user_invalid = 335545136L;
const ISC_STATUS isc_overriding_system_missing = 335545137L;
const ISC_STATUS isc_gfix_db_name = 335740929L;
const ISC_STATUS isc_gfix_invalid_sw = 335740930L;
const ISC_STATUS isc_gfix_incmp_sw = 335740932L;
@ -1343,7 +1347,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 = 1287;
const ISC_STATUS isc_err_max = 1291;
#else /* c definitions */
@ -2182,6 +2186,10 @@ const ISC_STATUS isc_err_max = 1287;
#define isc_att_shut_idle 335545131L
#define isc_att_shut_db_down 335545132L
#define isc_att_shut_engine 335545133L
#define isc_overriding_without_identity 335545134L
#define isc_overriding_system_invalid 335545135L
#define isc_overriding_user_invalid 335545136L
#define isc_overriding_system_missing 335545137L
#define isc_gfix_db_name 335740929L
#define isc_gfix_invalid_sw 335740930L
#define isc_gfix_incmp_sw 335740932L
@ -2656,7 +2664,7 @@ const ISC_STATUS isc_err_max = 1287;
#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 1287
#define isc_err_max 1291
#endif

View File

@ -838,6 +838,10 @@ Data source : @4"}, /* eds_statement */
{335545131, "Idle timeout expired."}, /* att_shut_idle */
{335545132, "Database is shutdown."}, /* att_shut_db_down */
{335545133, "Engine is shutdown."}, /* att_shut_engine */
{335545134, "OVERRIDING clause can be used only when an identity column is present in the INSERT's field list for table/view @1"}, /* overriding_without_identity */
{335545135, "OVERRIDING SYSTEM VALUE can be used only for identity column defined as 'GENERATED ALWAYS' in INSERT for table/view @1"}, /* overriding_system_invalid */
{335545136, "OVERRIDING USER VALUE can be used only for identity column defined as 'GENERATED BY DEFAULT' in INSERT for table/view"}, /* overriding_user_invalid */
{335545137, "OVERRIDING SYSTEM VALUE should be used to override the value of an identity column defined as 'GENERATED ALWAYS' in ta"}, /* overriding_system_missing */
{335740929, "data base file name (@1) already given"}, /* gfix_db_name */
{335740930, "invalid switch @1"}, /* gfix_invalid_sw */
{335740932, "incompatible switch combination"}, /* gfix_incmp_sw */

View File

@ -834,6 +834,10 @@ static const struct {
{335545131, -902}, /* 811 att_shut_idle */
{335545132, -902}, /* 812 att_shut_db_down */
{335545133, -902}, /* 813 att_shut_engine */
{335545134, -902}, /* 814 overriding_without_identity */
{335545135, -902}, /* 815 overriding_system_invalid */
{335545136, -902}, /* 816 overriding_user_invalid */
{335545137, -902}, /* 817 overriding_system_missing */
{335740929, -901}, /* 1 gfix_db_name */
{335740930, -901}, /* 2 gfix_invalid_sw */
{335740932, -901}, /* 4 gfix_incmp_sw */

View File

@ -834,6 +834,10 @@ static const struct {
{335545131, "08003"}, // 811 att_shut_idle
{335545132, "08003"}, // 812 att_shut_db_down
{335545133, "08003"}, // 813 att_shut_engine
{335545134, "42000"}, // 814 overriding_without_identity
{335545135, "42000"}, // 815 overriding_system_invalid
{335545136, "42000"}, // 816 overriding_user_invalid
{335545137, "42000"}, // 817 overriding_system_missing
{335740929, "00000"}, // 1 gfix_db_name
{335740930, "00000"}, // 2 gfix_invalid_sw
{335740932, "00000"}, // 4 gfix_incmp_sw

View File

@ -496,7 +496,9 @@ int EXTRACT_list_table(const SCHAR* relation_name,
FOR GEN IN RDB$GENERATORS
WITH GEN.RDB$GENERATOR_NAME = RFR.RDB$GENERATOR_NAME
{
isqlGlob.printf(" GENERATED BY DEFAULT AS IDENTITY");
isqlGlob.printf(" GENERATED %s AS IDENTITY",
(RFR.RDB$IDENTITY_TYPE == IDENT_TYPE_BY_DEFAULT ? "BY DEFAULT" :
RFR.RDB$IDENTITY_TYPE == IDENT_TYPE_ALWAYS ? "ALWAYS" : ""));
if (!GEN.RDB$INITIAL_VALUE.NULL && GEN.RDB$INITIAL_VALUE != 0)
isqlGlob.printf(" (START WITH %" SQUADFORMAT ")", GEN.RDB$INITIAL_VALUE);

View File

@ -5838,7 +5838,11 @@ static processing_state show_table(const SCHAR* relation_name, bool isView)
}
if (!RFR.RDB$GENERATOR_NAME.NULL)
isqlGlob.printf("Identity (by default)");
{
isqlGlob.printf("Identity (%s)",
(RFR.RDB$IDENTITY_TYPE == IDENT_TYPE_BY_DEFAULT ? "by default" :
RFR.RDB$IDENTITY_TYPE == IDENT_TYPE_ALWAYS ? "always" : ""));
}
// Handle defaults for columns

View File

@ -485,6 +485,7 @@ public:
Firebird::MetaName fld_security_name; // security class name for field
Firebird::MetaName fld_generator_name; // identity generator name
Firebird::MetaNamePair fld_source_rel_field; // Relation/field source name
Nullable<IdentityType> fld_identity_type;
public:
explicit jrd_fld(MemoryPool& p)

View File

@ -243,5 +243,6 @@ static const struct
{"gen_id2", gen_id2}, // 210
{"window_win", window_win},
{"default", relation_field},
{"store3", store3},
{0, 0}
};

View File

@ -421,5 +421,6 @@
#define blr_window_win_exclusion (unsigned char) 7
#define blr_default (unsigned char) 212
#define blr_store3 (unsigned char) 213
#endif // JRD_BLR_H

View File

@ -5754,6 +5754,10 @@ static bool make_version(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_
(UCHAR*) RFR.RDB$GENERATOR_NAME, n);
}
n = RFR.RDB$IDENTITY_TYPE;
if (!RFR.RDB$IDENTITY_TYPE.NULL)
put_summary_record(tdbb, blob, RSR_field_identity_type, (UCHAR*) &n, sizeof(n));
// Make a temporary field block
TemporaryField* tfb = FB_NEW_POOL(*tdbb->getDefaultPool()) TemporaryField;

View File

@ -449,6 +449,10 @@ ISC_STATUS filter_runtime(USHORT action, BlobControl* control)
sprintf(line, " field_generator_name: %s", p);
break;
case RSR_field_identity_type:
sprintf(line, "Field identity type: %d", n);
break;
default:
sprintf(line, "*** unknown verb %d ***", (int) buff[0]);
}

View File

@ -4046,6 +4046,12 @@ void MET_scan_relation(thread_db* tdbb, jrd_rel* relation)
case RSR_field_generator_name:
field->fld_generator_name = (const TEXT*) p;
if (!field->fld_identity_type.specified)
field->fld_identity_type = IDENT_TYPE_BY_DEFAULT;
break;
case RSR_field_identity_type:
field->fld_identity_type = static_cast<IdentityType>(n);
break;
default: // Shut up compiler warning

View File

@ -50,7 +50,8 @@ enum rsr_t {
RSR_field_length,
RSR_field_sub_type,
RSR_field_not_null,
RSR_field_generator_name
RSR_field_generator_name,
RSR_field_identity_type
};
// Temporary field block

View File

@ -1,7 +1,7 @@
/* MAX_NUMBER is the next number to be used, always one more than the highest message number. */
set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?);
--
('2016-12-20 12:57:00', 'JRD', 0, 814)
('2017-02-24 22:00:00', 'JRD', 0, 818)
('2015-03-17 18:33:00', 'QLI', 1, 533)
('2015-01-07 18:01:51', 'GFIX', 3, 134)
('1996-11-07 13:39:40', 'GPRE', 4, 1)

View File

@ -921,6 +921,10 @@ Data source : @4', NULL, NULL)
('att_shut_idle', NULL, 'jrd.cpp', NULL, 0, 811, NULL, 'Idle timeout expired.', NULL, NULL);
('att_shut_db_down', NULL, 'jrd.cpp', NULL, 0, 812, NULL, 'Database is shutdown.', NULL, NULL);
('att_shut_engine', NULL, 'jrd.cpp', NULL, 0, 813, NULL, 'Engine is shutdown.', NULL, NULL);
('overriding_without_identity', NULL, 'StmtNodes.cpp', NULL, 0, 814, NULL, 'OVERRIDING clause can be used only when an identity column is present in the INSERT''s field list for table/view @1', NULL, NULL);
('overriding_system_invalid', NULL, 'StmtNodes.cpp', NULL, 0, 815, NULL, 'OVERRIDING SYSTEM VALUE can be used only for identity column defined as ''GENERATED ALWAYS'' in INSERT for table/view @1', NULL, NULL);
('overriding_user_invalid', NULL, 'StmtNodes.cpp', NULL, 0, 816, NULL, 'OVERRIDING USER VALUE can be used only for identity column defined as ''GENERATED BY DEFAULT'' in INSERT for table/view @1', NULL, NULL);
('overriding_system_missing', NULL, 'StmtNodes.cpp', NULL, 0, 817, NULL, 'OVERRIDING SYSTEM VALUE should be used to override the value of an identity column defined as ''GENERATED ALWAYS'' in table/view @1', NULL, NULL);
-- QLI
(NULL, NULL, NULL, NULL, 1, 0, NULL, 'expected type', NULL, NULL);
(NULL, NULL, NULL, NULL, 1, 1, NULL, 'bad block type', NULL, NULL);

View File

@ -820,6 +820,10 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA
(-902, '08', '003', 0, 811, 'att_shut_idle', NULL, NULL)
(-902, '08', '003', 0, 812, 'att_shut_db_down', NULL, NULL)
(-902, '08', '003', 0, 813, 'att_shut_engine', NULL, NULL)
(-902, '42', '000', 0, 814, 'overriding_without_identity', NULL, NULL)
(-902, '42', '000', 0, 815, 'overriding_system_invalid', NULL, NULL)
(-902, '42', '000', 0, 816, 'overriding_user_invalid', NULL, NULL)
(-902, '42', '000', 0, 817, 'overriding_system_missing', NULL, NULL)
-- GFIX
(-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL)
(-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL)

View File

@ -344,7 +344,8 @@ static const UCHAR
subfunc_decl[] = { op_subfunc_decl, 0},
window_win[] = { op_byte, op_window_win, 0},
relation_field[] = { op_line, op_indent, op_byte, op_literal,
op_line, op_indent, op_byte, op_literal, op_pad, op_line, 0};
op_line, op_indent, op_byte, op_literal, op_pad, op_line, 0},
store3[] = { op_line, op_byte, op_line, op_verb, op_verb, op_verb, 0};
#include "../jrd/blp.h"

View File

@ -309,6 +309,7 @@ static const TOK tokens[] =
{TOK_OVER, "OVER", false},
{TOK_OVERFLOW, "OVERFLOW", true},
{TOK_OVERLAY, "OVERLAY", true},
{TOK_OVERRIDING, "OVERRIDING", true},
{TOK_PACKAGE, "PACKAGE", true},
{TOK_PAD, "PAD", true},
{TOK_PAGE, "PAGE", true},