Skip to content

Commit 4e2eb84

Browse files
HCK-13011: check constaint on field level (#168)
* add base implementation to support insile check constr from field level * add support script generation options for check constraints from field level
1 parent 1156462 commit 4e2eb84

File tree

8 files changed

+769
-11
lines changed

8 files changed

+769
-11
lines changed

forward_engineering/alterScript/alterScriptHelpers/alterEntityHelper.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const { getUpdateTypesScriptDtos } = require('./columnHelpers/alterTypeHelper');
55
const { getModifyNonNullColumnsScriptDtos } = require('./columnHelpers/nonNullConstraintHelper');
66
const { getModifiedCommentOnColumnScriptDtos } = require('./columnHelpers/commentsHelper');
77
const { getRenameColumnScriptDtos } = require('./columnHelpers/renameColumnHelper');
8+
const { getModifyColumnCheckConstraintScriptDtos } = require('./columnHelpers/checkConstraintHelper');
89
const { AlterScriptDto } = require('../types/AlterScriptDto');
910
const { AlterCollectionDto } = require('../types/AlterCollectionDto');
1011
const { getModifyPkConstraintsScriptDtos } = require('./entityHelpers/primaryKeyHelper');
@@ -303,12 +304,14 @@ const getModifyColumnScriptDtos =
303304
const modifyDefaultColumnValueScriptDtos = getModifiedDefaultColumnValueScriptDtos({
304305
collection,
305306
});
307+
const modifyColumnCheckConstraintScriptDtos = getModifyColumnCheckConstraintScriptDtos(collection);
306308

307309
return [
308310
...renameColumnScriptDtos,
309311
...updateTypeScriptDtos,
310312
...modifyNotNullScriptDtos,
311313
...modifyDefaultColumnValueScriptDtos,
314+
...modifyColumnCheckConstraintScriptDtos,
312315
...modifyCommentScriptDtos,
313316
].filter(Boolean);
314317
};
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
const _ = require('lodash');
2+
const { AlterScriptDto } = require('../../types/AlterScriptDto');
3+
const {
4+
getFullTableName,
5+
wrapInQuotes,
6+
isObjectInDeltaModelActivated,
7+
isParentContainerActivated,
8+
} = require('../../../utils/general');
9+
const assignTemplates = require('../../../utils/assignTemplates');
10+
const templates = require('../../../ddlProvider/templates');
11+
12+
/**
13+
* @typedef {{
14+
* name: string,
15+
* expression: string,
16+
* noInherit?: boolean,
17+
* }} ColumnCheckConstraint
18+
*
19+
* @typedef {{
20+
* old?: ColumnCheckConstraint,
21+
* new?: ColumnCheckConstraint,
22+
* columnName: string,
23+
* isActivated: boolean,
24+
* }} ColumnCheckConstraintHistoryEntry
25+
* */
26+
27+
/**
28+
*
29+
* @param {string} [constraintName]
30+
* @param {string} columnName
31+
* @param {string} tableName
32+
* @returns {string}
33+
*/
34+
const getConstraintName = (constraintName, columnName, tableName) => {
35+
return wrapInQuotes(constraintName || `chk_${tableName}.${columnName}`);
36+
};
37+
38+
/**
39+
* @param {string} tableName
40+
* @param {string} constraintName
41+
* @return string
42+
* */
43+
const dropConstraint = (tableName, constraintName) => {
44+
const templateConfig = {
45+
tableName,
46+
constraintName,
47+
};
48+
return assignTemplates(templates.dropConstraint, templateConfig);
49+
};
50+
51+
/**
52+
* @param tableName {string}
53+
* @param constraintName {string}
54+
* @param expression {string}
55+
* @param noInherit {boolean}
56+
* @return string
57+
* */
58+
const addCheckConstraint = (tableName, constraintName, expression, noInherit = false) => {
59+
const templateConfig = {
60+
tableName,
61+
constraintName,
62+
expression,
63+
noInherit: noInherit ? ' NO INHERIT' : '',
64+
};
65+
return assignTemplates(templates.addCheckConstraint, templateConfig);
66+
};
67+
68+
/**
69+
* @param {Object} collection
70+
* @return {ColumnCheckConstraintHistoryEntry[]}
71+
* */
72+
const mapColumnCheckConstraintsToChangeHistory = collection => {
73+
const history = [];
74+
75+
_.toPairs(collection.properties).forEach(([columnName, jsonSchema]) => {
76+
const oldColumnName = jsonSchema.compMod?.oldField?.name || columnName;
77+
const newCheckConstraint = !_.isEmpty(jsonSchema.checkConstraint)
78+
? _.omit(_.first(jsonSchema.checkConstraint), 'id')
79+
: undefined;
80+
81+
const oldCheckConstraintValue = collection.role.properties?.[oldColumnName]?.checkConstraint;
82+
const oldCheckConstraint = !_.isEmpty(oldCheckConstraintValue)
83+
? _.omit(_.first(oldCheckConstraintValue), 'id')
84+
: undefined;
85+
86+
if (!newCheckConstraint && !oldCheckConstraint) {
87+
return;
88+
}
89+
90+
history.push({
91+
columnName,
92+
old: oldCheckConstraint,
93+
new: newCheckConstraint,
94+
isActivated: jsonSchema.isActivated,
95+
});
96+
});
97+
98+
return history;
99+
};
100+
101+
/**
102+
* @param {ColumnCheckConstraintHistoryEntry[]} constraintHistory
103+
* @param {string} fullTableName
104+
* @return {AlterScriptDto[]}
105+
* */
106+
const getDropColumnCheckConstraintScriptDtos = (constraintHistory, fullTableName) => {
107+
return constraintHistory
108+
.filter(historyEntry => historyEntry.old && !historyEntry.new)
109+
.map(historyEntry => {
110+
const wrappedConstraintName = getConstraintName(
111+
historyEntry.old.name,
112+
historyEntry.columnName,
113+
fullTableName,
114+
);
115+
const script = dropConstraint(fullTableName, wrappedConstraintName);
116+
return AlterScriptDto.getInstance([script], historyEntry.isActivated, true);
117+
});
118+
};
119+
120+
/**
121+
* @param {ColumnCheckConstraintHistoryEntry[]} constraintHistory
122+
* @param {string} fullTableName
123+
* @return {AlterScriptDto[]}
124+
* */
125+
const getAddColumnCheckConstraintScriptDtos = (constraintHistory, fullTableName) => {
126+
return constraintHistory
127+
.filter(historyEntry => historyEntry.new && !historyEntry.old)
128+
.map(historyEntry => {
129+
const { name, expression, noInherit } = historyEntry.new;
130+
const constraintName = getConstraintName(name, historyEntry.columnName, fullTableName);
131+
132+
const script = addCheckConstraint(fullTableName, constraintName, expression, noInherit);
133+
return AlterScriptDto.getInstance([script], historyEntry.isActivated);
134+
});
135+
};
136+
137+
/**
138+
* @param {ColumnCheckConstraintHistoryEntry[]} constraintHistory
139+
* @param {string} fullTableName
140+
* @return {AlterScriptDto[]}
141+
* */
142+
const getUpdateColumnCheckConstraintScriptDtos = (constraintHistory, fullTableName) => {
143+
return constraintHistory
144+
.filter(historyEntry => {
145+
if (historyEntry.old && historyEntry.new) {
146+
const oldExpression = historyEntry.old.expression;
147+
const newExpression = historyEntry.new.expression;
148+
const oldNoInherit = historyEntry.old.noInherit;
149+
const newNoInherit = historyEntry.new.noInherit;
150+
const oldName = historyEntry.old.name;
151+
const newName = historyEntry.new.name;
152+
return oldExpression !== newExpression || oldNoInherit !== newNoInherit || oldName !== newName;
153+
}
154+
return false;
155+
})
156+
.map(historyEntry => {
157+
const { name: oldConstrainName } = historyEntry.old;
158+
const wrappedOldConstraintName = getConstraintName(
159+
oldConstrainName,
160+
historyEntry.columnName,
161+
fullTableName,
162+
);
163+
const dropConstraintScript = dropConstraint(fullTableName, wrappedOldConstraintName);
164+
165+
const {
166+
name: newConstrainName,
167+
expression: newConstraintExpression,
168+
noInherit: newNoInherit,
169+
} = historyEntry.new;
170+
const addConstraintScript = addCheckConstraint(
171+
fullTableName,
172+
getConstraintName(newConstrainName, historyEntry.columnName, fullTableName),
173+
newConstraintExpression,
174+
newNoInherit,
175+
);
176+
177+
return [
178+
AlterScriptDto.getInstance([dropConstraintScript], historyEntry.isActivated, true),
179+
AlterScriptDto.getInstance([addConstraintScript], historyEntry.isActivated, false),
180+
];
181+
})
182+
.flat();
183+
};
184+
185+
/**
186+
* @param {Object} collection
187+
* @return {AlterScriptDto[]}
188+
* */
189+
const getModifyColumnCheckConstraintScriptDtos = collection => {
190+
const fullTableName = getFullTableName(collection);
191+
const constraintHistory = mapColumnCheckConstraintsToChangeHistory(collection);
192+
193+
const isContainerActivated = isParentContainerActivated(collection);
194+
const isCollectionActivated = isObjectInDeltaModelActivated(collection);
195+
196+
const addCheckConstraintScripts = getAddColumnCheckConstraintScriptDtos(constraintHistory, fullTableName);
197+
const dropCheckConstraintScripts = getDropColumnCheckConstraintScriptDtos(constraintHistory, fullTableName);
198+
const updateCheckConstraintScripts = getUpdateColumnCheckConstraintScriptDtos(constraintHistory, fullTableName);
199+
200+
return [...addCheckConstraintScripts, ...dropCheckConstraintScripts, ...updateCheckConstraintScripts].map(dto => ({
201+
...dto,
202+
isActivated: isContainerActivated && isCollectionActivated && dto.isActivated,
203+
}));
204+
};
205+
206+
module.exports = {
207+
getModifyColumnCheckConstraintScriptDtos,
208+
};

forward_engineering/alterScript/alterScriptHelpers/entityHelpers/checkConstraintHelper.js

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const templates = require('../../../ddlProvider/templates');
1515
* id: string,
1616
* chkConstrName: string,
1717
* constrExpression: string,
18+
* noInherit?: boolean,
1819
* }} CheckConstraint
1920
*
2021
* @typedef {{
@@ -79,13 +80,15 @@ const getDropCheckConstraintScriptDtos = (constraintHistory, fullTableName) => {
7980
* @param tableName {string}
8081
* @param constraintName {string}
8182
* @param expression {expression}
83+
* @param noInherit {boolean}
8284
* @return string
8385
* */
84-
const addCheckConstraint = (tableName, constraintName, expression) => {
86+
const addCheckConstraint = (tableName, constraintName, expression, noInherit = false) => {
8587
const templateConfig = {
8688
tableName,
8789
constraintName,
8890
expression,
91+
noInherit: noInherit ? ' NO INHERIT' : '',
8992
};
9093
return assignTemplates(templates.addCheckConstraint, templateConfig);
9194
};
@@ -99,8 +102,8 @@ const getAddCheckConstraintScriptDtos = (constraintHistory, fullTableName) => {
99102
return constraintHistory
100103
.filter(historyEntry => historyEntry.new && !historyEntry.old)
101104
.map(historyEntry => {
102-
const { chkConstrName, constrExpression } = historyEntry.new;
103-
return addCheckConstraint(fullTableName, wrapInQuotes(chkConstrName), constrExpression);
105+
const { chkConstrName, constrExpression, noInherit } = historyEntry.new;
106+
return addCheckConstraint(fullTableName, wrapInQuotes(chkConstrName), constrExpression, noInherit);
104107
})
105108
.map(script => AlterScriptDto.getInstance([script], true, false));
106109
};
@@ -116,19 +119,26 @@ const getUpdateCheckConstraintScriptDtos = (constraintHistory, fullTableName) =>
116119
if (historyEntry.old && historyEntry.new) {
117120
const oldExpression = historyEntry.old.constrExpression;
118121
const newExpression = historyEntry.new.constrExpression;
119-
return oldExpression !== newExpression;
122+
const oldNoInherit = historyEntry.old.noInherit;
123+
const newNoInherit = historyEntry.new.noInherit;
124+
return oldExpression !== newExpression || oldNoInherit !== newNoInherit;
120125
}
121126
return false;
122127
})
123128
.map(historyEntry => {
124129
const { chkConstrName: oldConstrainName } = historyEntry.old;
125130
const dropConstraintScript = dropConstraint(fullTableName, wrapInQuotes(oldConstrainName));
126131

127-
const { chkConstrName: newConstrainName, constrExpression: newConstraintExpression } = historyEntry.new;
132+
const {
133+
chkConstrName: newConstrainName,
134+
constrExpression: newConstraintExpression,
135+
noInherit: newNoInherit,
136+
} = historyEntry.new;
128137
const addConstraintScript = addCheckConstraint(
129138
fullTableName,
130139
wrapInQuotes(newConstrainName),
131140
newConstraintExpression,
141+
newNoInherit,
132142
);
133143

134144
return [

forward_engineering/config.json

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,11 +245,21 @@
245245
"adapters": [
246246
{
247247
"dependency": {
248-
"key": "chkConstr",
249-
"valueType": "array"
248+
"type": "or",
249+
"values": [
250+
{
251+
"key": "chkConstr",
252+
"valueType": "array"
253+
},
254+
{
255+
"key": "checkConstraint",
256+
"valueType": "array"
257+
}
258+
]
250259
},
251260
"defaultValue": {
252-
"chkConstr": []
261+
"chkConstr": [],
262+
"checkConstraint": []
253263
}
254264
}
255265
]

forward_engineering/ddlProvider/ddlHelpers/constraintsHelper.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,30 @@ const createKeyConstraint = (templates, isParentActivated) => keyData => {
100100
};
101101
};
102102

103+
/**
104+
* Cleans the check constraint expression by removing surrounding parentheses and trimming whitespace.
105+
* @param expression {string}
106+
* @returns string
107+
*/
108+
const cleanCheckConstraint = (expression = '') => _.trim(expression).replace(/^\(([\s\S]*)\)$/, '$1');
109+
110+
/**
111+
* Creates an inline check constraint statement.
112+
* @param checkConstraint {{
113+
* name?: string,
114+
* expression?: string,
115+
* noInherit?: boolean,
116+
* }}
117+
* @returns string
118+
*/
119+
const createInlineCheckConstraint = checkConstraint => {
120+
if (!checkConstraint?.expression) {
121+
return '';
122+
}
123+
124+
return ` CHECK (${cleanCheckConstraint(checkConstraint.expression)})${checkConstraint?.noInherit ? ' NO INHERIT' : ''}`;
125+
};
126+
103127
/**
104128
* @param tableName {string}
105129
* @param isParentActivated {boolean}
@@ -196,4 +220,6 @@ module.exports = {
196220
dropKeyConstraint,
197221
getConstraintsWarnings,
198222
additionalPropertiesForForeignKey,
223+
cleanCheckConstraint,
224+
createInlineCheckConstraint,
199225
};

0 commit comments

Comments
 (0)