Skip to content

Commit

Permalink
Creation of not enforced constraints
Browse files Browse the repository at this point in the history
  • Loading branch information
aafemt committed Apr 25, 2024
1 parent bebb87d commit bfb319e
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 32 deletions.
42 changes: 39 additions & 3 deletions doc/sql.extensions/README.ddl.txt
Original file line number Diff line number Diff line change
Expand Up @@ -705,13 +705,49 @@ CREATE [GLOBAL] MAPPING [IF NOT EXISTS] ...
ALTER TABLE <table> ADD [IF NOT EXISTS] <column name> ...
ALTER TABLE <table> ADD CONSTRAINT [IF NOT EXISTS] <constraint name> ...

3) ALTER CONSTRAINT clause for ALTER TABLE statement.
3) Non-enforced constraints.

CREATE/ALTER TABLE supports creation of non-enforced constraints.

Syntax:

<col_constraint> ::=
[CONSTRAINT constr_name]
{ PRIMARY KEY [<using_index>]
| UNIQUE [<using_index>]
| REFERENCES other_table [(colname)] [<using_index>]
[ON DELETE {NO ACTION | CASCADE | SET DEFAULT | SET NULL}]
[ON UPDATE {NO ACTION | CASCADE | SET DEFAULT | SET NULL}]
| CHECK (<check_condition>)
| NOT NULL }
[<constraint characteristics>]

<tconstraint> ::=
[CONSTRAINT constr_name]
{ PRIMARY KEY (<col_list>) [<using_index>]
| UNIQUE (<col_list>) [<using_index>]
| FOREIGN KEY (<col_list>)
REFERENCES other_table [(<col_list>)] [<using_index>]
[ON DELETE {NO ACTION | CASCADE | SET DEFAULT | SET NULL}]
[ON UPDATE {NO ACTION | CASCADE | SET DEFAULT | SET NULL}]
| CHECK (<check_condition>) }
[<constraint characteristics>]

<constraint characteristics> ::=
<constraint enforcement>

<constraint enforcement> ::=
[ NOT ] ENFORCED

Note: In contrast to ANSI SQL standard PRIMARY KEY and UNIQUE constraint
are allowed to be not enforced.

Also ALTER CONSTRAINT clause is added to ALTER TABLE statement.

Syntax:

ALTER TABLE ALTER CONSTRAINT <constraint name> [NOT] ENFORCED
ALTER TABLE ALTER CONSTRAINT <constraint name> <constraint enforcement>

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

The corresponding ALTER INDEX and ALTER TRIGGER statements are allowed as well.
Expand Down
13 changes: 12 additions & 1 deletion src/dsql/DdlNodes.epp
Original file line number Diff line number Diff line change
Expand Up @@ -6714,11 +6714,18 @@ void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
constraint.create = FB_NEW_POOL(pool) Constraint(pool);
constraint.create->type = Constraint::TYPE_NOT_NULL;
if (clause->constraintType == AddConstraintClause::CTYPE_NOT_NULL)
{
constraint.name = clause->name;
constraint.create->enforced = clause->enforced;
*notNull = clause->enforced;
}
// NOT NULL for PRIMARY KEY is always enforced
}

if (clause->constraintType == AddConstraintClause::CTYPE_NOT_NULL)
{
break;
}
// AddConstraintClause::CTYPE_PK falls into

case AddConstraintClause::CTYPE_UNIQUE:
Expand All @@ -6732,6 +6739,7 @@ void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
if (constraint.create->index && constraint.create->index->name.isEmpty())
constraint.create->index->name = constraint.name;
constraint.create->columns = clause->columns;
constraint.create->enforced = clause->enforced;
break;
}

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

// If there is a referenced table name but no referenced field names, the
// primary key of the referenced table designates the referenced fields.
Expand Down Expand Up @@ -6858,6 +6867,7 @@ void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
CreateDropConstraint& constraint = constraints.add();
constraint.create = FB_NEW_POOL(pool) Constraint(pool);
constraint.create->type = Constraint::TYPE_CHECK;
constraint.create->enforced = clause->enforced;
constraint.name = clause->name;
defineCheckConstraint(dsqlScratch, *constraint.create, clause->check);
break;
Expand Down Expand Up @@ -6924,7 +6934,7 @@ void RelationNode::defineConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlSc
definition.unique = constraint.type != Constraint::TYPE_FK;
if (constraint.index->descending)
definition.descending = true;
definition.inactive = false;
definition.inactive = !constraint.enforced;
definition.columns = constraint.columns;
definition.refRelation = constraint.refRelation;
definition.refColumns = constraint.refColumns;
Expand Down Expand Up @@ -7185,6 +7195,7 @@ void RelationNode::defineCheckConstraintTrigger(DsqlCompilerScratch* dsqlScratch
trigger.type = triggerType;
trigger.source = clause->source;
trigger.blrData = blrWriter.getBlrData();
trigger.active = constraint.enforced;
}

// Define "on delete|update set default" trigger (for referential integrity) along with its blr.
Expand Down
2 changes: 2 additions & 0 deletions src/dsql/DdlNodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1300,6 +1300,7 @@ class RelationNode : public DdlNode
const char* refDeleteAction;
Firebird::ObjectsArray<TriggerDefinition> triggers;
Firebird::ObjectsArray<BlrWriter> blrWritersHolder;
bool enforced = true;
};

struct CreateDropConstraint
Expand Down Expand Up @@ -1389,6 +1390,7 @@ class RelationNode : public DdlNode
NestConst<RefActionClause> refAction;
NestConst<BoolSourceClause> check;
bool createIfNotExistsOnly = false;
bool enforced = true;
};

struct IdentityOptions
Expand Down
2 changes: 1 addition & 1 deletion src/dsql/parse-conflicts.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
115 shift/reduce conflicts, 22 reduce/reduce conflicts.
116 shift/reduce conflicts, 22 reduce/reduce conflicts.
26 changes: 23 additions & 3 deletions src/dsql/parse.y
Original file line number Diff line number Diff line change
Expand Up @@ -2624,23 +2624,26 @@ column_constraint_def($addColumnClause)
: constraint_name_opt column_constraint($addColumnClause)
{
if ($1)
$addColumnClause->constraints.back().name = *$1;
$2->name = *$1;
}
constraint_characteristics_opt($2)
;

%type column_constraint(<addColumnClause>)
%type <addConstraintClause> column_constraint(<addColumnClause>)
column_constraint($addColumnClause)
: null_constraint
{
setClause($addColumnClause->notNullSpecified, "NOT NULL");
RelationNode::AddConstraintClause& constraint = $addColumnClause->constraints.add();
constraint.constraintType = RelationNode::AddConstraintClause::CTYPE_NOT_NULL;
$$ = &constraint;
}
| check_constraint
{
RelationNode::AddConstraintClause& constraint = $addColumnClause->constraints.add();
constraint.constraintType = RelationNode::AddConstraintClause::CTYPE_CHECK;
constraint.check = $1;
$$ = &constraint;
}
| REFERENCES symbol_table_name column_parens_opt
referential_trigger_action constraint_index_opt
Expand All @@ -2662,18 +2665,21 @@ column_constraint($addColumnClause)
}

constraint.index = $5;
$$ = &constraint;
}
| UNIQUE constraint_index_opt
{
RelationNode::AddConstraintClause& constraint = $addColumnClause->constraints.add();
constraint.constraintType = RelationNode::AddConstraintClause::CTYPE_UNIQUE;
constraint.index = $2;
$$ = &constraint;
}
| PRIMARY KEY constraint_index_opt
{
RelationNode::AddConstraintClause& constraint = $addColumnClause->constraints.add();
constraint.constraintType = RelationNode::AddConstraintClause::CTYPE_PK;
constraint.index = $3;
$$ = &constraint;
}
;

Expand All @@ -2688,6 +2694,10 @@ table_constraint_definition($relationNode)
$2->name = *$1;
$$ = $2;
}
constraint_characteristics_opt($3)
{
$$ = $3;
}
;

%type <metaNamePtr> constraint_name_opt
Expand Down Expand Up @@ -2786,6 +2796,15 @@ constraint_index_opt
***/
;

%type constraint_characteristics_opt(<addConstraintClause>)
constraint_characteristics_opt($addConstraintClause)
: // nothing
| constraint_enforcement
{
$addConstraintClause->enforced = $1;
}
;

%type <refActionClause> referential_trigger_action
referential_trigger_action
: /* nothing */ { $$ = NULL; }
Expand Down Expand Up @@ -4384,13 +4403,14 @@ alter_op($relationNode)
const auto node = $3;
node->createIfNotExistsOnly = $2;
}
| ADD table_constraint($relationNode)
| ADD table_constraint($relationNode) constraint_characteristics_opt($2)
| ADD CONSTRAINT if_not_exists_opt symbol_constraint_name table_constraint($relationNode)
{
const auto node = $5;
node->name = *$4;
node->createIfNotExistsOnly = $3;
}
constraint_characteristics_opt($5)
| col_opt alter_column_name POSITION pos_short_integer
{
RelationNode::AlterColPosClause* clause = newNode<RelationNode::AlterColPosClause>();
Expand Down
72 changes: 48 additions & 24 deletions src/isql/extract.epp
Original file line number Diff line number Diff line change
Expand Up @@ -548,33 +548,42 @@ int EXTRACT_list_table(const SCHAR* relation_name,
** rdb$check_constraints. We hope we get at most one row back.
*/

if (RFR.RDB$NULL_FLAG == 1)
{
FOR RCO IN RDB$RELATION_CONSTRAINTS CROSS
CON IN RDB$CHECK_CONSTRAINTS WITH
CON.RDB$TRIGGER_NAME = RFR.RDB$FIELD_NAME AND
CON.RDB$CONSTRAINT_NAME = RCO.RDB$CONSTRAINT_NAME AND
RCO.RDB$CONSTRAINT_TYPE EQ "NOT NULL" AND
RCO.RDB$RELATION_NAME = RFR.RDB$RELATION_NAME
bool found = false;

FOR RCO IN RDB$RELATION_CONSTRAINTS CROSS
CON IN RDB$CHECK_CONSTRAINTS WITH
CON.RDB$TRIGGER_NAME = RFR.RDB$FIELD_NAME AND
CON.RDB$CONSTRAINT_NAME = RCO.RDB$CONSTRAINT_NAME AND
RCO.RDB$CONSTRAINT_TYPE EQ "NOT NULL" AND
RCO.RDB$RELATION_NAME = RFR.RDB$RELATION_NAME

if (!fb_utils::implicit_integrity(CON.RDB$CONSTRAINT_NAME))
if (!fb_utils::implicit_integrity(CON.RDB$CONSTRAINT_NAME))
{
fb_utils::exact_name(CON.RDB$CONSTRAINT_NAME);
if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
{
fb_utils::exact_name(CON.RDB$CONSTRAINT_NAME);
if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
{
IUTILS_copy_SQL_id (CON.RDB$CONSTRAINT_NAME, SQL_identifier, DBL_QUOTE);
isqlGlob.printf(" CONSTRAINT %s", SQL_identifier);
}
else
isqlGlob.printf(" CONSTRAINT %s", CON.RDB$CONSTRAINT_NAME);
IUTILS_copy_SQL_id (CON.RDB$CONSTRAINT_NAME, SQL_identifier, DBL_QUOTE);
isqlGlob.printf(" CONSTRAINT %s", SQL_identifier);
}
END_FOR
ON_ERROR
ISQL_errmsg (fbStatus);
return FINI_ERROR;
END_ERROR;
else
isqlGlob.printf(" CONSTRAINT %s", CON.RDB$CONSTRAINT_NAME);
}
found = true;

END_FOR
ON_ERROR
ISQL_errmsg (fbStatus);
return FINI_ERROR;
END_ERROR;

if (found)
{
isqlGlob.printf(" NOT NULL");

if (RFR.RDB$NULL_FLAG == 0)
{
isqlGlob.printf(" NOT ENFORCED");
}
}

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

if (IDX.RDB$INDEX_INACTIVE == 1)
{
isqlGlob.printf(" NOT ENFORCED");
}

END_FOR
ON_ERROR
ISQL_errmsg(fbStatus);
Expand Down Expand Up @@ -1926,6 +1940,11 @@ static void list_check()
if (!TRG.RDB$TRIGGER_SOURCE.NULL)
SHOW_print_metadata_text_blob (isqlGlob.Out, &TRG.RDB$TRIGGER_SOURCE);

if (TRG.RDB$TRIGGER_INACTIVE == 1)
{
isqlGlob.printf(" NOT ENFORCED");
}

isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE);

END_FOR
Expand Down Expand Up @@ -2828,12 +2847,14 @@ static void list_foreign()

FOR RELC1 IN RDB$RELATION_CONSTRAINTS CROSS
RELC2 IN RDB$RELATION_CONSTRAINTS CROSS
REFC IN RDB$REF_CONSTRAINTS WITH
REFC IN RDB$REF_CONSTRAINTS CROSS
IDX IN RDB$INDICES WITH
RELC1.RDB$CONSTRAINT_TYPE EQ "FOREIGN KEY" AND
REFC.RDB$CONST_NAME_UQ EQ RELC2.RDB$CONSTRAINT_NAME AND
REFC.RDB$CONSTRAINT_NAME EQ RELC1.RDB$CONSTRAINT_NAME AND
(RELC2.RDB$CONSTRAINT_TYPE EQ "UNIQUE" OR
RELC2.RDB$CONSTRAINT_TYPE EQ "PRIMARY KEY")
RELC2.RDB$CONSTRAINT_TYPE EQ "PRIMARY KEY") AND
IDX.RDB$INDEX_NAME = RELC1.RDB$INDEX_NAME
SORTED BY RELC1.RDB$RELATION_NAME, RELC1.RDB$CONSTRAINT_NAME

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

if (IDX.RDB$INDEX_INACTIVE == 1)
isqlGlob.printf(" NOT ENFORCED");

isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE);

END_FOR
Expand Down

0 comments on commit bfb319e

Please sign in to comment.