Skip to content

Commit bfb319e

Browse files
committed
Creation of not enforced constraints
1 parent bebb87d commit bfb319e

File tree

6 files changed

+125
-32
lines changed

6 files changed

+125
-32
lines changed

Diff for: doc/sql.extensions/README.ddl.txt

+39-3
Original file line numberDiff line numberDiff line change
@@ -705,13 +705,49 @@ CREATE [GLOBAL] MAPPING [IF NOT EXISTS] ...
705705
ALTER TABLE <table> ADD [IF NOT EXISTS] <column name> ...
706706
ALTER TABLE <table> ADD CONSTRAINT [IF NOT EXISTS] <constraint name> ...
707707

708-
3) ALTER CONSTRAINT clause for ALTER TABLE statement.
708+
3) Non-enforced constraints.
709+
710+
CREATE/ALTER TABLE supports creation of non-enforced constraints.
711+
712+
Syntax:
713+
714+
<col_constraint> ::=
715+
[CONSTRAINT constr_name]
716+
{ PRIMARY KEY [<using_index>]
717+
| UNIQUE [<using_index>]
718+
| REFERENCES other_table [(colname)] [<using_index>]
719+
[ON DELETE {NO ACTION | CASCADE | SET DEFAULT | SET NULL}]
720+
[ON UPDATE {NO ACTION | CASCADE | SET DEFAULT | SET NULL}]
721+
| CHECK (<check_condition>)
722+
| NOT NULL }
723+
[<constraint characteristics>]
724+
725+
<tconstraint> ::=
726+
[CONSTRAINT constr_name]
727+
{ PRIMARY KEY (<col_list>) [<using_index>]
728+
| UNIQUE (<col_list>) [<using_index>]
729+
| FOREIGN KEY (<col_list>)
730+
REFERENCES other_table [(<col_list>)] [<using_index>]
731+
[ON DELETE {NO ACTION | CASCADE | SET DEFAULT | SET NULL}]
732+
[ON UPDATE {NO ACTION | CASCADE | SET DEFAULT | SET NULL}]
733+
| CHECK (<check_condition>) }
734+
[<constraint characteristics>]
735+
736+
<constraint characteristics> ::=
737+
<constraint enforcement>
738+
739+
<constraint enforcement> ::=
740+
[ NOT ] ENFORCED
741+
742+
Note: In contrast to ANSI SQL standard PRIMARY KEY and UNIQUE constraint
743+
are allowed to be not enforced.
744+
745+
Also ALTER CONSTRAINT clause is added to ALTER TABLE statement.
709746

710747
Syntax:
711748

712-
ALTER TABLE ALTER CONSTRAINT <constraint name> [NOT] ENFORCED
749+
ALTER TABLE ALTER CONSTRAINT <constraint name> <constraint enforcement>
713750

714-
Supported for UNIQUE, PRIMARY KEY, FOREIGN KEY, CHECK AND NOT NULL constraints.
715751
Primary and unique keys cannot be deactivated if they are referenced by any active foreign key.
716752

717753
The corresponding ALTER INDEX and ALTER TRIGGER statements are allowed as well.

Diff for: src/dsql/DdlNodes.epp

+12-1
Original file line numberDiff line numberDiff line change
@@ -6714,11 +6714,18 @@ void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
67146714
constraint.create = FB_NEW_POOL(pool) Constraint(pool);
67156715
constraint.create->type = Constraint::TYPE_NOT_NULL;
67166716
if (clause->constraintType == AddConstraintClause::CTYPE_NOT_NULL)
6717+
{
67176718
constraint.name = clause->name;
6719+
constraint.create->enforced = clause->enforced;
6720+
*notNull = clause->enforced;
6721+
}
6722+
// NOT NULL for PRIMARY KEY is always enforced
67186723
}
67196724

67206725
if (clause->constraintType == AddConstraintClause::CTYPE_NOT_NULL)
6726+
{
67216727
break;
6728+
}
67226729
// AddConstraintClause::CTYPE_PK falls into
67236730

67246731
case AddConstraintClause::CTYPE_UNIQUE:
@@ -6732,6 +6739,7 @@ void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
67326739
if (constraint.create->index && constraint.create->index->name.isEmpty())
67336740
constraint.create->index->name = constraint.name;
67346741
constraint.create->columns = clause->columns;
6742+
constraint.create->enforced = clause->enforced;
67356743
break;
67366744
}
67376745

@@ -6744,6 +6752,7 @@ void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
67446752
constraint.create->columns = clause->columns;
67456753
constraint.create->refRelation = clause->refRelation;
67466754
constraint.create->refColumns = clause->refColumns;
6755+
constraint.create->enforced = clause->enforced;
67476756

67486757
// If there is a referenced table name but no referenced field names, the
67496758
// primary key of the referenced table designates the referenced fields.
@@ -6858,6 +6867,7 @@ void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
68586867
CreateDropConstraint& constraint = constraints.add();
68596868
constraint.create = FB_NEW_POOL(pool) Constraint(pool);
68606869
constraint.create->type = Constraint::TYPE_CHECK;
6870+
constraint.create->enforced = clause->enforced;
68616871
constraint.name = clause->name;
68626872
defineCheckConstraint(dsqlScratch, *constraint.create, clause->check);
68636873
break;
@@ -6924,7 +6934,7 @@ void RelationNode::defineConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlSc
69246934
definition.unique = constraint.type != Constraint::TYPE_FK;
69256935
if (constraint.index->descending)
69266936
definition.descending = true;
6927-
definition.inactive = false;
6937+
definition.inactive = !constraint.enforced;
69286938
definition.columns = constraint.columns;
69296939
definition.refRelation = constraint.refRelation;
69306940
definition.refColumns = constraint.refColumns;
@@ -7185,6 +7195,7 @@ void RelationNode::defineCheckConstraintTrigger(DsqlCompilerScratch* dsqlScratch
71857195
trigger.type = triggerType;
71867196
trigger.source = clause->source;
71877197
trigger.blrData = blrWriter.getBlrData();
7198+
trigger.active = constraint.enforced;
71887199
}
71897200

71907201
// Define "on delete|update set default" trigger (for referential integrity) along with its blr.

Diff for: src/dsql/DdlNodes.h

+2
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,7 @@ class RelationNode : public DdlNode
13001300
const char* refDeleteAction;
13011301
Firebird::ObjectsArray<TriggerDefinition> triggers;
13021302
Firebird::ObjectsArray<BlrWriter> blrWritersHolder;
1303+
bool enforced = true;
13031304
};
13041305

13051306
struct CreateDropConstraint
@@ -1389,6 +1390,7 @@ class RelationNode : public DdlNode
13891390
NestConst<RefActionClause> refAction;
13901391
NestConst<BoolSourceClause> check;
13911392
bool createIfNotExistsOnly = false;
1393+
bool enforced = true;
13921394
};
13931395

13941396
struct IdentityOptions

Diff for: src/dsql/parse-conflicts.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
115 shift/reduce conflicts, 22 reduce/reduce conflicts.
1+
116 shift/reduce conflicts, 22 reduce/reduce conflicts.

Diff for: src/dsql/parse.y

+23-3
Original file line numberDiff line numberDiff line change
@@ -2624,23 +2624,26 @@ column_constraint_def($addColumnClause)
26242624
: constraint_name_opt column_constraint($addColumnClause)
26252625
{
26262626
if ($1)
2627-
$addColumnClause->constraints.back().name = *$1;
2627+
$2->name = *$1;
26282628
}
2629+
constraint_characteristics_opt($2)
26292630
;
26302631

2631-
%type column_constraint(<addColumnClause>)
2632+
%type <addConstraintClause> column_constraint(<addColumnClause>)
26322633
column_constraint($addColumnClause)
26332634
: null_constraint
26342635
{
26352636
setClause($addColumnClause->notNullSpecified, "NOT NULL");
26362637
RelationNode::AddConstraintClause& constraint = $addColumnClause->constraints.add();
26372638
constraint.constraintType = RelationNode::AddConstraintClause::CTYPE_NOT_NULL;
2639+
$$ = &constraint;
26382640
}
26392641
| check_constraint
26402642
{
26412643
RelationNode::AddConstraintClause& constraint = $addColumnClause->constraints.add();
26422644
constraint.constraintType = RelationNode::AddConstraintClause::CTYPE_CHECK;
26432645
constraint.check = $1;
2646+
$$ = &constraint;
26442647
}
26452648
| REFERENCES symbol_table_name column_parens_opt
26462649
referential_trigger_action constraint_index_opt
@@ -2662,18 +2665,21 @@ column_constraint($addColumnClause)
26622665
}
26632666

26642667
constraint.index = $5;
2668+
$$ = &constraint;
26652669
}
26662670
| UNIQUE constraint_index_opt
26672671
{
26682672
RelationNode::AddConstraintClause& constraint = $addColumnClause->constraints.add();
26692673
constraint.constraintType = RelationNode::AddConstraintClause::CTYPE_UNIQUE;
26702674
constraint.index = $2;
2675+
$$ = &constraint;
26712676
}
26722677
| PRIMARY KEY constraint_index_opt
26732678
{
26742679
RelationNode::AddConstraintClause& constraint = $addColumnClause->constraints.add();
26752680
constraint.constraintType = RelationNode::AddConstraintClause::CTYPE_PK;
26762681
constraint.index = $3;
2682+
$$ = &constraint;
26772683
}
26782684
;
26792685

@@ -2688,6 +2694,10 @@ table_constraint_definition($relationNode)
26882694
$2->name = *$1;
26892695
$$ = $2;
26902696
}
2697+
constraint_characteristics_opt($3)
2698+
{
2699+
$$ = $3;
2700+
}
26912701
;
26922702

26932703
%type <metaNamePtr> constraint_name_opt
@@ -2786,6 +2796,15 @@ constraint_index_opt
27862796
***/
27872797
;
27882798

2799+
%type constraint_characteristics_opt(<addConstraintClause>)
2800+
constraint_characteristics_opt($addConstraintClause)
2801+
: // nothing
2802+
| constraint_enforcement
2803+
{
2804+
$addConstraintClause->enforced = $1;
2805+
}
2806+
;
2807+
27892808
%type <refActionClause> referential_trigger_action
27902809
referential_trigger_action
27912810
: /* nothing */ { $$ = NULL; }
@@ -4384,13 +4403,14 @@ alter_op($relationNode)
43844403
const auto node = $3;
43854404
node->createIfNotExistsOnly = $2;
43864405
}
4387-
| ADD table_constraint($relationNode)
4406+
| ADD table_constraint($relationNode) constraint_characteristics_opt($2)
43884407
| ADD CONSTRAINT if_not_exists_opt symbol_constraint_name table_constraint($relationNode)
43894408
{
43904409
const auto node = $5;
43914410
node->name = *$4;
43924411
node->createIfNotExistsOnly = $3;
43934412
}
4413+
constraint_characteristics_opt($5)
43944414
| col_opt alter_column_name POSITION pos_short_integer
43954415
{
43964416
RelationNode::AlterColPosClause* clause = newNode<RelationNode::AlterColPosClause>();

Diff for: src/isql/extract.epp

+48-24
Original file line numberDiff line numberDiff line change
@@ -548,33 +548,42 @@ int EXTRACT_list_table(const SCHAR* relation_name,
548548
** rdb$check_constraints. We hope we get at most one row back.
549549
*/
550550

551-
if (RFR.RDB$NULL_FLAG == 1)
552-
{
553-
FOR RCO IN RDB$RELATION_CONSTRAINTS CROSS
554-
CON IN RDB$CHECK_CONSTRAINTS WITH
555-
CON.RDB$TRIGGER_NAME = RFR.RDB$FIELD_NAME AND
556-
CON.RDB$CONSTRAINT_NAME = RCO.RDB$CONSTRAINT_NAME AND
557-
RCO.RDB$CONSTRAINT_TYPE EQ "NOT NULL" AND
558-
RCO.RDB$RELATION_NAME = RFR.RDB$RELATION_NAME
551+
bool found = false;
552+
553+
FOR RCO IN RDB$RELATION_CONSTRAINTS CROSS
554+
CON IN RDB$CHECK_CONSTRAINTS WITH
555+
CON.RDB$TRIGGER_NAME = RFR.RDB$FIELD_NAME AND
556+
CON.RDB$CONSTRAINT_NAME = RCO.RDB$CONSTRAINT_NAME AND
557+
RCO.RDB$CONSTRAINT_TYPE EQ "NOT NULL" AND
558+
RCO.RDB$RELATION_NAME = RFR.RDB$RELATION_NAME
559559

560-
if (!fb_utils::implicit_integrity(CON.RDB$CONSTRAINT_NAME))
560+
if (!fb_utils::implicit_integrity(CON.RDB$CONSTRAINT_NAME))
561+
{
562+
fb_utils::exact_name(CON.RDB$CONSTRAINT_NAME);
563+
if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
561564
{
562-
fb_utils::exact_name(CON.RDB$CONSTRAINT_NAME);
563-
if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
564-
{
565-
IUTILS_copy_SQL_id (CON.RDB$CONSTRAINT_NAME, SQL_identifier, DBL_QUOTE);
566-
isqlGlob.printf(" CONSTRAINT %s", SQL_identifier);
567-
}
568-
else
569-
isqlGlob.printf(" CONSTRAINT %s", CON.RDB$CONSTRAINT_NAME);
565+
IUTILS_copy_SQL_id (CON.RDB$CONSTRAINT_NAME, SQL_identifier, DBL_QUOTE);
566+
isqlGlob.printf(" CONSTRAINT %s", SQL_identifier);
570567
}
571-
END_FOR
572-
ON_ERROR
573-
ISQL_errmsg (fbStatus);
574-
return FINI_ERROR;
575-
END_ERROR;
568+
else
569+
isqlGlob.printf(" CONSTRAINT %s", CON.RDB$CONSTRAINT_NAME);
570+
}
571+
found = true;
576572

573+
END_FOR
574+
ON_ERROR
575+
ISQL_errmsg (fbStatus);
576+
return FINI_ERROR;
577+
END_ERROR;
578+
579+
if (found)
580+
{
577581
isqlGlob.printf(" NOT NULL");
582+
583+
if (RFR.RDB$NULL_FLAG == 0)
584+
{
585+
isqlGlob.printf(" NOT ENFORCED");
586+
}
578587
}
579588

580589
// Handle collations after defaults
@@ -681,6 +690,11 @@ static bool extract_rel_constraints(const char* relation_name)
681690
isqlGlob.printf(" %s", IDX.RDB$INDEX_NAME);
682691
}
683692

693+
if (IDX.RDB$INDEX_INACTIVE == 1)
694+
{
695+
isqlGlob.printf(" NOT ENFORCED");
696+
}
697+
684698
END_FOR
685699
ON_ERROR
686700
ISQL_errmsg(fbStatus);
@@ -1926,6 +1940,11 @@ static void list_check()
19261940
if (!TRG.RDB$TRIGGER_SOURCE.NULL)
19271941
SHOW_print_metadata_text_blob (isqlGlob.Out, &TRG.RDB$TRIGGER_SOURCE);
19281942

1943+
if (TRG.RDB$TRIGGER_INACTIVE == 1)
1944+
{
1945+
isqlGlob.printf(" NOT ENFORCED");
1946+
}
1947+
19291948
isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE);
19301949

19311950
END_FOR
@@ -2828,12 +2847,14 @@ static void list_foreign()
28282847

28292848
FOR RELC1 IN RDB$RELATION_CONSTRAINTS CROSS
28302849
RELC2 IN RDB$RELATION_CONSTRAINTS CROSS
2831-
REFC IN RDB$REF_CONSTRAINTS WITH
2850+
REFC IN RDB$REF_CONSTRAINTS CROSS
2851+
IDX IN RDB$INDICES WITH
28322852
RELC1.RDB$CONSTRAINT_TYPE EQ "FOREIGN KEY" AND
28332853
REFC.RDB$CONST_NAME_UQ EQ RELC2.RDB$CONSTRAINT_NAME AND
28342854
REFC.RDB$CONSTRAINT_NAME EQ RELC1.RDB$CONSTRAINT_NAME AND
28352855
(RELC2.RDB$CONSTRAINT_TYPE EQ "UNIQUE" OR
2836-
RELC2.RDB$CONSTRAINT_TYPE EQ "PRIMARY KEY")
2856+
RELC2.RDB$CONSTRAINT_TYPE EQ "PRIMARY KEY") AND
2857+
IDX.RDB$INDEX_NAME = RELC1.RDB$INDEX_NAME
28372858
SORTED BY RELC1.RDB$RELATION_NAME, RELC1.RDB$CONSTRAINT_NAME
28382859

28392860
fb_utils::exact_name(RELC1.RDB$RELATION_NAME);
@@ -2893,6 +2914,9 @@ static void list_foreign()
28932914
ISQL_ri_action_print (REFC.RDB$DELETE_RULE, " ON DELETE", true);
28942915
}
28952916

2917+
if (IDX.RDB$INDEX_INACTIVE == 1)
2918+
isqlGlob.printf(" NOT ENFORCED");
2919+
28962920
isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE);
28972921

28982922
END_FOR

0 commit comments

Comments
 (0)