@@ -109,6 +109,8 @@ static void updateRdbFields(const TypeClause* type,
109
109
SSHORT& fieldPrecisionNull, SSHORT& fieldPrecision,
110
110
SSHORT& collationIdNull, SSHORT& collationId,
111
111
SSHORT& segmentLengthNull, SSHORT& segmentLength);
112
+ static void modifyIndex(thread_db* tdbb, jrd_tra* transaction,
113
+ const char* name, bool active);
112
114
113
115
static const char* const CHECK_CONSTRAINT_EXCEPTION = "check_constraint";
114
116
@@ -177,6 +179,50 @@ void ExecInSecurityDb::executeInSecurityDb(jrd_tra* localTransaction)
177
179
//----------------------
178
180
179
181
182
+ // Activate/deactivate given index
183
+ static void modifyIndex(thread_db* tdbb, jrd_tra* transaction,
184
+ const QualifiedName& name, bool active)
185
+ {
186
+ AutoCacheRequest request(tdbb, drq_m_index, DYN_REQUESTS);
187
+
188
+ bool found = false;
189
+ FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
190
+ IDX IN RDB$INDICES
191
+ WITH IDX.RDB$SCHEMA_NAME EQUIV name.schema.c_str() AND
192
+ IDX.RDB$INDEX_NAME EQ name.object.c_str()
193
+ {
194
+ found = true;
195
+ MODIFY IDX
196
+ IDX.RDB$INDEX_INACTIVE.NULL = FALSE;
197
+ IDX.RDB$INDEX_INACTIVE = active ? FALSE : TRUE;
198
+ END_MODIFY
199
+ }
200
+ END_FOR
201
+
202
+ if (!found)
203
+ {
204
+ // msg 48: "Index not found"
205
+ status_exception::raise(Arg::PrivateDyn(48) << Arg::Gds(isc_index_name) << name.toQuotedString());
206
+ }
207
+ }
208
+
209
+ // Check if given index is referenced by active foreign key constraint
210
+ static void checkIndexReferenced(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name)
211
+ {
212
+ AutoCacheRequest fkCheck(tdbb, drq_c_active_fk, DYN_REQUESTS);
213
+
214
+ FOR(REQUEST_HANDLE fkCheck TRANSACTION_HANDLE transaction)
215
+ IDX IN RDB$INDICES
216
+ WITH IDX.RDB$FOREIGN_KEY_SCHEMA_NAME EQ name.schema.c_str() AND
217
+ IDX.RDB$FOREIGN_KEY EQ name.object.c_str() AND
218
+ IDX.RDB$INDEX_INACTIVE EQ 0 OR IDX.RDB$INDEX_INACTIVE MISSING
219
+ {
220
+ // MSG 408: "Can't deactivate index used by an integrity constraint"
221
+ status_exception::raise(Arg::Gds(isc_integ_index_deactivate));
222
+ }
223
+ END_FOR
224
+ }
225
+
180
226
// Check temporary table reference rules between given child relation and master
181
227
// relation (owner of given PK/UK index).
182
228
static void checkForeignKeyTempScope(thread_db* tdbb, jrd_tra* transaction,
@@ -3616,7 +3662,6 @@ bool TriggerDefinition::modify(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch
3616
3662
{
3617
3663
switch (TRG.RDB$SYSTEM_FLAG)
3618
3664
{
3619
- case fb_sysflag_check_constraint:
3620
3665
case fb_sysflag_referential_constraint:
3621
3666
case fb_sysflag_view_check:
3622
3667
status_exception::raise(Arg::Gds(isc_dyn_cant_modify_auto_trig));
@@ -6440,6 +6485,7 @@ DdlNode* RelationNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
6440
6485
case Clause::TYPE_ALTER_COL_NAME:
6441
6486
case Clause::TYPE_ALTER_COL_NULL:
6442
6487
case Clause::TYPE_ALTER_COL_POS:
6488
+ case Clause::TYPE_ALTER_CONSTRAINT:
6443
6489
case Clause::TYPE_DROP_COLUMN:
6444
6490
case Clause::TYPE_DROP_CONSTRAINT:
6445
6491
case Clause::TYPE_ALTER_SQL_SECURITY:
@@ -6888,7 +6934,12 @@ void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
6888
6934
constraint.create = FB_NEW_POOL(pool) Constraint(pool);
6889
6935
constraint.create->type = Constraint::TYPE_NOT_NULL;
6890
6936
if (clause->constraintType == AddConstraintClause::CTYPE_NOT_NULL)
6937
+ {
6891
6938
constraint.name = clause->name;
6939
+ constraint.create->enforced = clause->enforced;
6940
+ *notNull = clause->enforced;
6941
+ }
6942
+ // NOT NULL for PRIMARY KEY is always enforced
6892
6943
}
6893
6944
6894
6945
if (clause->constraintType == AddConstraintClause::CTYPE_NOT_NULL)
@@ -6908,6 +6959,7 @@ void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
6908
6959
constraint.create->index->name = constraint.name;
6909
6960
6910
6961
constraint.create->columns = clause->columns;
6962
+ constraint.create->enforced = clause->enforced;
6911
6963
break;
6912
6964
}
6913
6965
@@ -6920,6 +6972,7 @@ void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
6920
6972
constraint.create->columns = clause->columns;
6921
6973
constraint.create->refRelation = clause->refRelation;
6922
6974
constraint.create->refColumns = clause->refColumns;
6975
+ constraint.create->enforced = clause->enforced;
6923
6976
6924
6977
// If there is a referenced table name but no referenced field names, the
6925
6978
// primary key of the referenced table designates the referenced fields.
@@ -7035,6 +7088,7 @@ void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
7035
7088
CreateDropConstraint& constraint = constraints.add();
7036
7089
constraint.create = FB_NEW_POOL(pool) Constraint(pool);
7037
7090
constraint.create->type = Constraint::TYPE_CHECK;
7091
+ constraint.create->enforced = clause->enforced;
7038
7092
constraint.name = clause->name;
7039
7093
defineCheckConstraint(dsqlScratch, *constraint.create, clause->check);
7040
7094
break;
@@ -7106,7 +7160,7 @@ void RelationNode::defineConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlSc
7106
7160
definition.unique = constraint.type != Constraint::TYPE_FK;
7107
7161
if (constraint.index->descending)
7108
7162
definition.descending = true;
7109
- definition.inactive = false ;
7163
+ definition.inactive = !constraint.enforced ;
7110
7164
definition.columns = constraint.columns;
7111
7165
definition.refRelation = constraint.refRelation;
7112
7166
definition.refColumns = constraint.refColumns;
@@ -7381,6 +7435,7 @@ void RelationNode::defineCheckConstraintTrigger(DsqlCompilerScratch* dsqlScratch
7381
7435
trigger.type = triggerType;
7382
7436
trigger.source = clause->source;
7383
7437
trigger.blrData = blrWriter.getBlrData();
7438
+ trigger.active = constraint.enforced;
7384
7439
}
7385
7440
7386
7441
// Define "on delete|update set default" trigger (for referential integrity) along with its blr.
@@ -8292,6 +8347,123 @@ void AlterRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
8292
8347
break;
8293
8348
}
8294
8349
8350
+ case Clause::TYPE_ALTER_CONSTRAINT:
8351
+ {
8352
+ executeBeforeTrigger();
8353
+
8354
+ const AlterConstraintClause* clause = static_cast<const AlterConstraintClause*>(i->getObject());
8355
+ AutoCacheRequest request(tdbb, drq_get_constr_type, DYN_REQUESTS);
8356
+ bool found = false;
8357
+
8358
+ FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
8359
+ RC IN RDB$RELATION_CONSTRAINTS
8360
+ WITH RC.RDB$CONSTRAINT_NAME EQ clause->name.c_str() AND
8361
+ RC.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
8362
+ RC.RDB$RELATION_NAME EQ name.object.c_str()
8363
+ {
8364
+ found = true;
8365
+ QualifiedName indexName(RC.RDB$INDEX_NAME, RC.RDB$SCHEMA_NAME);
8366
+ fb_utils::exact_name(RC.RDB$CONSTRAINT_TYPE);
8367
+ if (strcmp(RC.RDB$CONSTRAINT_TYPE, PRIMARY_KEY) == 0 ||
8368
+ strcmp(RC.RDB$CONSTRAINT_TYPE, UNIQUE_CNSTRT) == 0)
8369
+ {
8370
+ // Deactivation of primary/unique key requires check for active foreign keys
8371
+ checkIndexReferenced(tdbb, transaction, indexName);
8372
+ modifyIndex(tdbb, transaction, indexName, clause->enforced);
8373
+ }
8374
+ else if (strcmp(RC.RDB$CONSTRAINT_TYPE, FOREIGN_KEY) == 0)
8375
+ {
8376
+ // Activation of foreign key requires check for active partner which is done on index activation
8377
+ // so there is nothing to check here
8378
+ modifyIndex(tdbb, transaction, indexName, clause->enforced);
8379
+ }
8380
+ else if (strcmp(RC.RDB$CONSTRAINT_TYPE, CHECK_CNSTRT) == 0)
8381
+ {
8382
+ AutoCacheRequest requestHandle(tdbb, drq_m_check_trgs, DYN_REQUESTS);
8383
+
8384
+ FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
8385
+ TRG IN RDB$TRIGGERS CROSS
8386
+ CHK IN RDB$CHECK_CONSTRAINTS
8387
+ WITH TRG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
8388
+ TRG.RDB$RELATION_NAME EQ name.object.c_str() AND
8389
+ TRG.RDB$SCHEMA_NAME EQ CHK.RDB$SCHEMA_NAME AND
8390
+ TRG.RDB$TRIGGER_NAME EQ CHK.RDB$TRIGGER_NAME AND
8391
+ CHK.RDB$CONSTRAINT_NAME EQ clause->name.c_str()
8392
+ {
8393
+ MODIFY TRG
8394
+ TRG.RDB$TRIGGER_INACTIVE = clause->enforced ? FALSE : TRUE;
8395
+ END_MODIFY
8396
+ }
8397
+ END_FOR
8398
+ }
8399
+ else if (strcmp(RC.RDB$CONSTRAINT_TYPE, NOT_NULL_CNSTRT) == 0)
8400
+ {
8401
+ AutoRequest requestHandle;
8402
+
8403
+ FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
8404
+ CHK IN RDB$CHECK_CONSTRAINTS CROSS
8405
+ RF IN RDB$RELATION_FIELDS
8406
+ WITH CHK.RDB$CONSTRAINT_NAME EQ clause->name.c_str() AND
8407
+ CHK.RDB$SCHEMA_NAME EQ RF.RDB$SCHEMA_NAME AND
8408
+ CHK.RDB$TRIGGER_NAME EQ RF.RDB$FIELD_NAME AND
8409
+ RF.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
8410
+ RF.RDB$RELATION_NAME EQ name.object.c_str()
8411
+ {
8412
+ // Identity column cannot be NULL-able.
8413
+ if (RF.RDB$IDENTITY_TYPE.NULL == FALSE)
8414
+ {
8415
+ fb_utils::exact_name(RF.RDB$FIELD_NAME);
8416
+ // msg 274: Identity column @1 of table @2 cannot be changed to NULLable
8417
+ status_exception::raise(Arg::PrivateDyn(274) << RF.RDB$FIELD_NAME << name.toQuotedString());
8418
+ }
8419
+
8420
+ // Column of an active primary key cannot be nullable
8421
+ AutoRequest request3;
8422
+
8423
+ FOR (REQUEST_HANDLE request3 TRANSACTION_HANDLE transaction)
8424
+ ISG IN RDB$INDEX_SEGMENTS
8425
+ CROSS IDX IN RDB$INDICES OVER RDB$SCHEMA_NAME, RDB$INDEX_NAME
8426
+ CROSS RC2 IN RDB$RELATION_CONSTRAINTS OVER RDB$SCHEMA_NAME, RDB$INDEX_NAME
8427
+ WITH ISG.RDB$FIELD_NAME EQ RF.RDB$FIELD_NAME AND
8428
+ IDX.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
8429
+ IDX.RDB$RELATION_NAME EQ name.object.c_str() AND
8430
+ (IDX.RDB$INDEX_INACTIVE EQ 0 OR IDX.RDB$INDEX_INACTIVE MISSING) AND
8431
+ RC2.RDB$CONSTRAINT_TYPE EQ PRIMARY_KEY
8432
+ {
8433
+ status_exception::raise(Arg::Gds(isc_primary_key_notnull));
8434
+ }
8435
+ END_FOR
8436
+
8437
+ // Otherwise it is fine
8438
+ MODIFY RF
8439
+ if (clause->enforced)
8440
+ {
8441
+ RF.RDB$NULL_FLAG.NULL = FALSE;
8442
+ RF.RDB$NULL_FLAG = TRUE;
8443
+ }
8444
+ else
8445
+ {
8446
+ RF.RDB$NULL_FLAG.NULL = TRUE;
8447
+ RF.RDB$NULL_FLAG = FALSE; // For symmetry
8448
+ }
8449
+ END_MODIFY
8450
+ }
8451
+ END_FOR
8452
+ }
8453
+ else
8454
+ status_exception::raise(Arg::Gds(isc_wish_list) << Arg::Gds(isc_ref_cnstrnt_update));
8455
+ }
8456
+ END_FOR
8457
+
8458
+ if (!found)
8459
+ {
8460
+ // msg 130: "CONSTRAINT %s does not exist."
8461
+ status_exception::raise(Arg::PrivateDyn(130) << clause->name);
8462
+ }
8463
+
8464
+ break;
8465
+ }
8466
+
8295
8467
case Clause::TYPE_ALTER_SQL_SECURITY:
8296
8468
{
8297
8469
executeBeforeTrigger();
@@ -10569,6 +10741,8 @@ void AlterIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
10569
10741
10570
10742
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_INDEX, name, {});
10571
10743
10744
+ checkIndexReferenced(tdbb, transaction, name);
10745
+
10572
10746
MODIFY IDX
10573
10747
IDX.RDB$INDEX_INACTIVE.NULL = FALSE;
10574
10748
IDX.RDB$INDEX_INACTIVE = active ? FALSE : TRUE;
0 commit comments