@@ -112,6 +112,8 @@ static void updateRdbFields(const TypeClause* type,
112
112
SSHORT& fieldPrecisionNull, SSHORT& fieldPrecision,
113
113
SSHORT& collationIdNull, SSHORT& collationId,
114
114
SSHORT& segmentLengthNull, SSHORT& segmentLength);
115
+ static void modifyIndex(thread_db* tdbb, jrd_tra* transaction,
116
+ const char* name, bool active);
115
117
116
118
static const char* const CHECK_CONSTRAINT_EXCEPTION = "check_constraint";
117
119
@@ -179,6 +181,47 @@ void ExecInSecurityDb::executeInSecurityDb(jrd_tra* localTransaction)
179
181
180
182
//----------------------
181
183
184
+ // Activate/deactivate given index
185
+ static void modifyIndex(thread_db* tdbb, jrd_tra* transaction,
186
+ const char* name, bool active)
187
+ {
188
+ AutoCacheRequest request(tdbb, drq_m_index, DYN_REQUESTS);
189
+
190
+ bool found = false;
191
+ FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
192
+ IDX IN RDB$INDICES
193
+ WITH IDX.RDB$INDEX_NAME EQ name
194
+ {
195
+ found = true;
196
+ MODIFY IDX
197
+ IDX.RDB$INDEX_INACTIVE.NULL = FALSE;
198
+ IDX.RDB$INDEX_INACTIVE = active ? FALSE : TRUE;
199
+ END_MODIFY
200
+ }
201
+ END_FOR
202
+
203
+ if (!found)
204
+ {
205
+ // msg 48: "Index not found"
206
+ status_exception::raise(Arg::PrivateDyn(48));
207
+ }
208
+ }
209
+
210
+ // Check if given index is referenced by active foreign key constraint
211
+ static void checkIndexReferenced(thread_db* tdbb, jrd_tra* transaction, const char* name)
212
+ {
213
+ AutoCacheRequest fkCheck(tdbb, drq_c_active_fk, DYN_REQUESTS);
214
+
215
+ FOR(REQUEST_HANDLE fkCheck TRANSACTION_HANDLE transaction)
216
+ IDX IN RDB$INDICES
217
+ WITH IDX.RDB$FOREIGN_KEY EQ name 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
+ }
182
225
183
226
// Check temporary table reference rules between given child relation and master
184
227
// relation (owner of given PK/UK index).
@@ -3525,7 +3568,6 @@ bool TriggerDefinition::modify(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch
3525
3568
{
3526
3569
switch (TRG.RDB$SYSTEM_FLAG)
3527
3570
{
3528
- case fb_sysflag_check_constraint:
3529
3571
case fb_sysflag_referential_constraint:
3530
3572
case fb_sysflag_view_check:
3531
3573
status_exception::raise(Arg::Gds(isc_dyn_cant_modify_auto_trig));
@@ -6648,11 +6690,18 @@ void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
6648
6690
constraint.create = FB_NEW_POOL(pool) Constraint(pool);
6649
6691
constraint.create->type = Constraint::TYPE_NOT_NULL;
6650
6692
if (clause->constraintType == AddConstraintClause::CTYPE_NOT_NULL)
6693
+ {
6651
6694
constraint.name = clause->name;
6695
+ constraint.create->enforced = clause->enforced;
6696
+ *notNull = clause->enforced;
6697
+ }
6698
+ // NOT NULL for PRIMARY KEY is always enforced
6652
6699
}
6653
6700
6654
6701
if (clause->constraintType == AddConstraintClause::CTYPE_NOT_NULL)
6702
+ {
6655
6703
break;
6704
+ }
6656
6705
// AddConstraintClause::CTYPE_PK falls into
6657
6706
6658
6707
case AddConstraintClause::CTYPE_UNIQUE:
@@ -6666,6 +6715,7 @@ void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
6666
6715
if (constraint.create->index && constraint.create->index->name.isEmpty())
6667
6716
constraint.create->index->name = constraint.name;
6668
6717
constraint.create->columns = clause->columns;
6718
+ constraint.create->enforced = clause->enforced;
6669
6719
break;
6670
6720
}
6671
6721
@@ -6678,6 +6728,7 @@ void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
6678
6728
constraint.create->columns = clause->columns;
6679
6729
constraint.create->refRelation = clause->refRelation;
6680
6730
constraint.create->refColumns = clause->refColumns;
6731
+ constraint.create->enforced = clause->enforced;
6681
6732
6682
6733
// If there is a referenced table name but no referenced field names, the
6683
6734
// primary key of the referenced table designates the referenced fields.
@@ -6792,6 +6843,7 @@ void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
6792
6843
CreateDropConstraint& constraint = constraints.add();
6793
6844
constraint.create = FB_NEW_POOL(pool) Constraint(pool);
6794
6845
constraint.create->type = Constraint::TYPE_CHECK;
6846
+ constraint.create->enforced = clause->enforced;
6795
6847
constraint.name = clause->name;
6796
6848
defineCheckConstraint(dsqlScratch, *constraint.create, clause->check);
6797
6849
break;
@@ -6858,7 +6910,7 @@ void RelationNode::defineConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlSc
6858
6910
definition.unique = constraint.type != Constraint::TYPE_FK;
6859
6911
if (constraint.index->descending)
6860
6912
definition.descending = true;
6861
- definition.inactive = false ;
6913
+ definition.inactive = !constraint.enforced ;
6862
6914
definition.columns = constraint.columns;
6863
6915
definition.refRelation = constraint.refRelation;
6864
6916
definition.refColumns = constraint.refColumns;
@@ -7119,6 +7171,7 @@ void RelationNode::defineCheckConstraintTrigger(DsqlCompilerScratch* dsqlScratch
7119
7171
trigger.type = triggerType;
7120
7172
trigger.source = clause->source;
7121
7173
trigger.blrData = blrWriter.getBlrData();
7174
+ trigger.active = constraint.enforced;
7122
7175
}
7123
7176
7124
7177
// Define "on delete|update set default" trigger (for referential integrity) along with its blr.
@@ -7979,6 +8032,118 @@ void AlterRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
7979
8032
break;
7980
8033
}
7981
8034
8035
+ case Clause::TYPE_ALTER_CONSTRAINT:
8036
+ {
8037
+ executeBeforeTrigger();
8038
+
8039
+ const AlterConstraintClause* clause = static_cast<const AlterConstraintClause*>(i->getObject());
8040
+ AutoCacheRequest request(tdbb, drq_get_constr_type, DYN_REQUESTS);
8041
+ bool found = false;
8042
+
8043
+ FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
8044
+ RC IN RDB$RELATION_CONSTRAINTS
8045
+ WITH RC.RDB$CONSTRAINT_NAME EQ clause->name.c_str() AND
8046
+ RC.RDB$RELATION_NAME EQ name.c_str()
8047
+ {
8048
+ found = true;
8049
+ fb_utils::exact_name(RC.RDB$CONSTRAINT_TYPE);
8050
+ if (strcmp(RC.RDB$CONSTRAINT_TYPE, PRIMARY_KEY) == 0 ||
8051
+ strcmp(RC.RDB$CONSTRAINT_TYPE, UNIQUE_CNSTRT) == 0)
8052
+ {
8053
+ // Deactivation of primary/unique key requires check for active foreign keys
8054
+ checkIndexReferenced(tdbb, transaction, RC.RDB$INDEX_NAME);
8055
+ modifyIndex(tdbb, transaction, RC.RDB$INDEX_NAME, clause->enforced);
8056
+ }
8057
+ else if (strcmp(RC.RDB$CONSTRAINT_TYPE, FOREIGN_KEY) == 0)
8058
+ {
8059
+ // Activation of foreign key requires check for active partner which is done on index activation
8060
+ // so there is nothing to check here
8061
+ modifyIndex(tdbb, transaction, RC.RDB$INDEX_NAME, clause->enforced);
8062
+ }
8063
+ else if (strcmp(RC.RDB$CONSTRAINT_TYPE, CHECK_CNSTRT) == 0)
8064
+ {
8065
+ AutoCacheRequest requestHandle(tdbb, drq_m_check_trgs, DYN_REQUESTS);
8066
+
8067
+ FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
8068
+ TRG IN RDB$TRIGGERS CROSS
8069
+ CHK IN RDB$CHECK_CONSTRAINTS
8070
+ WITH TRG.RDB$RELATION_NAME EQ name.c_str() AND
8071
+ TRG.RDB$TRIGGER_NAME EQ CHK.RDB$TRIGGER_NAME AND
8072
+ CHK.RDB$CONSTRAINT_NAME EQ clause->name.c_str()
8073
+ {
8074
+ MODIFY TRG
8075
+ TRG.RDB$TRIGGER_INACTIVE = clause->enforced ? FALSE : TRUE;
8076
+ END_MODIFY
8077
+ }
8078
+ END_FOR
8079
+ }
8080
+ else if (strcmp(RC.RDB$CONSTRAINT_TYPE, NOT_NULL_CNSTRT) == 0)
8081
+ {
8082
+ AutoRequest requestHandle;
8083
+
8084
+ FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
8085
+ CHK IN RDB$CHECK_CONSTRAINTS CROSS
8086
+ RF IN RDB$RELATION_FIELDS
8087
+ WITH CHK.RDB$CONSTRAINT_NAME EQ clause->name.c_str() AND
8088
+ CHK.RDB$TRIGGER_NAME EQ RF.RDB$FIELD_NAME AND
8089
+ RF.RDB$RELATION_NAME EQ name.c_str()
8090
+ {
8091
+ // Identity column cannot be NULL-able.
8092
+ if (RF.RDB$IDENTITY_TYPE.NULL == FALSE)
8093
+ {
8094
+ fb_utils::exact_name(RF.RDB$FIELD_NAME);
8095
+ // msg 274: Identity column @1 of table @2 cannot be changed to NULLable
8096
+ status_exception::raise(Arg::PrivateDyn(274) << RF.RDB$FIELD_NAME << name.c_str());
8097
+ }
8098
+
8099
+ // Column of an active primary key cannot be nullable
8100
+ AutoRequest request3;
8101
+
8102
+ FOR (REQUEST_HANDLE request3 TRANSACTION_HANDLE transaction)
8103
+ ISG IN RDB$INDEX_SEGMENTS CROSS
8104
+ IDX IN RDB$INDICES CROSS
8105
+ RC2 IN RDB$RELATION_CONSTRAINTS
8106
+ WITH ISG.RDB$FIELD_NAME EQ RF.RDB$FIELD_NAME AND
8107
+ ISG.RDB$INDEX_NAME EQ IDX.RDB$INDEX_NAME AND
8108
+ IDX.RDB$RELATION_NAME EQ name.c_str() AND
8109
+ (IDX.RDB$INDEX_INACTIVE EQ 0 OR IDX.RDB$INDEX_INACTIVE MISSING) AND
8110
+ RC2.RDB$INDEX_NAME EQ IDX.RDB$INDEX_NAME AND
8111
+ RC2.RDB$CONSTRAINT_TYPE EQ PRIMARY_KEY
8112
+ {
8113
+ status_exception::raise(Arg::Gds(isc_primary_key_notnull));
8114
+ }
8115
+ END_FOR
8116
+
8117
+ // Otherwise it is fine
8118
+ MODIFY RF
8119
+ if (clause->enforced)
8120
+ {
8121
+ RF.RDB$NULL_FLAG.NULL = FALSE;
8122
+ RF.RDB$NULL_FLAG = TRUE;
8123
+ }
8124
+ else
8125
+ {
8126
+ RF.RDB$NULL_FLAG.NULL = TRUE;
8127
+ RF.RDB$NULL_FLAG = FALSE; // For symmetry
8128
+ }
8129
+ END_MODIFY
8130
+ }
8131
+ END_FOR
8132
+ }
8133
+ else
8134
+ status_exception::raise(Arg::Gds(isc_wish_list) << Arg::Gds(isc_ref_cnstrnt_update));
8135
+ }
8136
+ END_FOR
8137
+
8138
+ if (!found)
8139
+ {
8140
+ // msg 130: "CONSTRAINT %s does not exist."
8141
+ status_exception::raise(Arg::PrivateDyn(130) << clause->name);
8142
+ }
8143
+
8144
+ break;
8145
+ }
8146
+
7982
8147
case Clause::TYPE_ALTER_SQL_SECURITY:
7983
8148
{
7984
8149
executeBeforeTrigger();
@@ -10152,6 +10317,8 @@ void AlterIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
10152
10317
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_INDEX,
10153
10318
name, NULL);
10154
10319
10320
+ checkIndexReferenced(tdbb, transaction, name.c_str());
10321
+
10155
10322
MODIFY IDX
10156
10323
IDX.RDB$INDEX_INACTIVE.NULL = FALSE;
10157
10324
IDX.RDB$INDEX_INACTIVE = active ? FALSE : TRUE;
0 commit comments