Skip to content

Commit eacb7fd

Browse files
authored
HCK-4516: Add support of computed columns (#126)
* HCK-4516: Add computedColumn to PP config * HCK-4516: Add Persisted keyword * HCK-4516: implement RE from instance * HCK-4516: Update FE script & rename propertis * HCK-4516: Add alter script * HCK-4516: Fix alter script * HCK-4516: Clean up the code * HCK-4516: Fix SQ warnings * HCK-4516: fix pr comment
1 parent 349ec7a commit eacb7fd

File tree

9 files changed

+506
-18
lines changed

9 files changed

+506
-18
lines changed

forward_engineering/configs/templates.js

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module.exports = {
1212

1313
columnDefinition:
1414
'[${name}] ${type}${primary_key}${temporalTableTime}${sparse}${maskedWithFunction}${identity}${default}${collation}${not_null}${encryptedWith}',
15+
computedColumnDefinition: '[${name}] AS ${expression}${persisted}',
1516

1617
index:
1718
'CREATE${unique}${clustered}${columnstore} INDEX ${name}\n' +
@@ -82,6 +83,8 @@ module.exports = {
8283

8384
alterColumn: 'ALTER COLUMN [${name}] ${type}${collation}${not_null}',
8485

86+
alterComputedColumn: 'ADD [${name}] AS ${expression}${persisted}',
87+
8588
renameColumn: "EXEC sp_rename '${fullTableName}.${oldColumnName}', '${newColumnName}', 'COLUMN';${terminator}",
8689

8790
dropView: 'DROP VIEW IF EXISTS ${name}${terminator}',

forward_engineering/ddlProvider.js

+50-14
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ module.exports = (baseProvider, options, app) => {
4040
const { wrapIfNotExistSchema, wrapIfNotExistDatabase, wrapIfNotExistTable, wrapIfNotExistView } =
4141
require('./helpers/ifNotExistStatementHelper')(app);
4242
const { getPartitionedTables, getCreateViewData } = require('./helpers/viewHelper')(app);
43-
const { getFullTableName, escapeSpecialCharacters } = require('./utils/general')(_);
43+
const { getFullTableName, escapeSpecialCharacters, wrapInBracketsIfNecessary } = require('./utils/general')(_);
4444

4545
const terminator = getTerminator(options);
4646

@@ -177,6 +177,14 @@ module.exports = (baseProvider, options, app) => {
177177
: fullTableStatement;
178178
},
179179

180+
createComputedColumn({ name, computedExpression, persisted }) {
181+
return assignTemplates(templates.computedColumnDefinition, {
182+
name,
183+
expression: wrapInBracketsIfNecessary(computedExpression),
184+
persisted: persisted ? ' PERSISTED' : '',
185+
});
186+
},
187+
180188
convertColumnDefinition(columnDefinition) {
181189
const type = hasType(columnDefinition.type)
182190
? _.toUpper(columnDefinition.type)
@@ -207,19 +215,28 @@ module.exports = (baseProvider, options, app) => {
207215
columnDefinition.isHidden,
208216
);
209217

210-
const statement = assignTemplates(templates.columnDefinition, {
211-
name: columnDefinition.name,
212-
type: decorateType(type, columnDefinition),
213-
primary_key: primaryKey + unique,
214-
not_null: notNull,
215-
default: defaultValue,
216-
sparse,
217-
maskedWithFunction,
218-
encryptedWith,
219-
terminator,
220-
temporalTableTime,
221-
...identityContainer,
222-
});
218+
const { name, persisted, computed, computedExpression } = columnDefinition;
219+
220+
const statement =
221+
computed && computedExpression
222+
? this.createComputedColumn({
223+
name,
224+
computedExpression,
225+
persisted,
226+
})
227+
: assignTemplates(templates.columnDefinition, {
228+
name,
229+
type: decorateType(type, columnDefinition),
230+
primary_key: primaryKey + unique,
231+
not_null: notNull,
232+
default: defaultValue,
233+
sparse,
234+
maskedWithFunction,
235+
encryptedWith,
236+
terminator,
237+
temporalTableTime,
238+
...identityContainer,
239+
});
223240

224241
return commentIfDeactivated(statement, { isActivated: columnDefinition.isActivated });
225242
},
@@ -464,6 +481,9 @@ module.exports = (baseProvider, options, app) => {
464481
encryption,
465482
hasMaxLength: columnDefinition.hasMaxLength || jsonSchema.type === 'jsonObject',
466483
comment: jsonSchema.description,
484+
computed: jsonSchema.computed,
485+
computedExpression: jsonSchema.computedExpression,
486+
persisted: jsonSchema.persisted,
467487
...(canHaveIdentity(jsonSchema.mode) && {
468488
identity: {
469489
seed: Number(_.get(jsonSchema, 'identity.identitySeed', 0)),
@@ -730,6 +750,22 @@ module.exports = (baseProvider, options, app) => {
730750
});
731751
},
732752

753+
alterComputedColumn(fullTableName, columnName, columnDefinition) {
754+
const { computedExpression, persisted } = columnDefinition;
755+
756+
const command = assignTemplates(templates.alterComputedColumn, {
757+
name: columnName,
758+
expression: wrapInBracketsIfNecessary(computedExpression),
759+
persisted: persisted ? ' PERSISTED' : '',
760+
});
761+
762+
return assignTemplates(templates.alterTable, {
763+
tableName: fullTableName,
764+
command,
765+
terminator,
766+
});
767+
},
768+
733769
dropView(fullViewName) {
734770
return assignTemplates(templates.dropView, {
735771
name: fullViewName,

forward_engineering/helpers/alterScriptHelpers/alterEntityHelper.js

+16-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ module.exports = (app, options) => {
99
const { generateIdToNameHashTable, generateIdToActivatedHashTable } = app.require('@hackolade/ddl-fe-utils');
1010
const { setIndexKeys, modifyGroupItems } = require('./common')(app);
1111
const { getRenameColumnScriptsDto } = require('./columnHelpers/renameColumnHelpers')(app, ddlProvider);
12+
const { getChangedComputedColumnsScriptsDto } = require('./columnHelpers/alterComputedColumnHelpr')(
13+
app,
14+
ddlProvider,
15+
);
1216
const { getChangeTypeScriptsDto } = require('./columnHelpers/alterTypeHelper')(app, ddlProvider);
1317
const { AlterScriptDto } = require('./types/AlterScriptDto');
1418
const { getModifyCheckConstraintScriptDtos } = require('./entityHelpers/checkConstraintHelper');
@@ -188,8 +192,19 @@ module.exports = (app, options) => {
188192
collectionSchema,
189193
schemaName,
190194
);
195+
const changedComputedScriptsDtos = getChangedComputedColumnsScriptsDto({
196+
collection,
197+
fullName,
198+
collectionSchema,
199+
schemaName,
200+
});
191201

192-
return [...renameColumnScriptsDtos, ...changeTypeScriptsDtos, ...modifyNotNullScriptDtos].filter(Boolean);
202+
return [
203+
...renameColumnScriptsDtos,
204+
...changeTypeScriptsDtos,
205+
...modifyNotNullScriptDtos,
206+
...changedComputedScriptsDtos,
207+
].filter(Boolean);
193208
};
194209

195210
const hydrateIndex =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
'use strict';
2+
3+
const _ = require('lodash');
4+
5+
module.exports = (app, ddlProvider) => {
6+
const { createColumnDefinitionBySchema } = require('./createColumnDefinition')(_);
7+
const { AlterScriptDto } = require('../types/AlterScriptDto');
8+
9+
const changeToComputed = (fullName, columnName, columnDefinition) => {
10+
return [
11+
AlterScriptDto.getInstance([ddlProvider.dropColumn(fullName, columnName)], true, true),
12+
AlterScriptDto.getInstance(
13+
[ddlProvider.alterComputedColumn(fullName, columnName, columnDefinition)],
14+
true,
15+
false,
16+
),
17+
];
18+
};
19+
20+
const changeToNonComputed = (fullName, columnName, columnDefinition) => {
21+
return [
22+
AlterScriptDto.getInstance([ddlProvider.dropColumn(fullName, columnName)], true, true),
23+
AlterScriptDto.getInstance([ddlProvider.alterColumn(fullName, columnDefinition)], true, false),
24+
];
25+
};
26+
27+
const generateSqlAlterScript = ({
28+
collectionSchema,
29+
prevJsonSchema,
30+
jsonSchema,
31+
fullName,
32+
columnName,
33+
schemaName,
34+
}) => {
35+
const schemaData = { schemaName };
36+
const columnDefinition = createColumnDefinitionBySchema({
37+
name: columnName,
38+
jsonSchema,
39+
parentJsonSchema: collectionSchema,
40+
ddlProvider,
41+
schemaData,
42+
});
43+
44+
let sqlScripts = [];
45+
46+
const isComputedRemoved = prevJsonSchema.computed && !jsonSchema.computed;
47+
const isComputedEnabled = !prevJsonSchema.computed && jsonSchema.computed;
48+
const isComputedModified =
49+
prevJsonSchema.computed &&
50+
jsonSchema.computed &&
51+
(prevJsonSchema.computedExpression !== jsonSchema.computedExpression ||
52+
prevJsonSchema.persisted !== jsonSchema.persisted);
53+
54+
if ((isComputedRemoved || isComputedModified) && !jsonSchema.computedExpression) {
55+
sqlScripts = changeToNonComputed(fullName, columnName, columnDefinition);
56+
}
57+
58+
if ((isComputedEnabled || isComputedModified) && jsonSchema.computedExpression) {
59+
sqlScripts = changeToComputed(fullName, columnName, columnDefinition);
60+
}
61+
62+
return sqlScripts;
63+
};
64+
65+
const getChangedComputedColumnsScriptsDto = ({ collection, fullName, collectionSchema, schemaName }) => {
66+
return _.flatten(
67+
_.toPairs(collection.properties).reduce((result, [columnName, jsonSchema]) => {
68+
const oldJsonSchema = _.omit(collection.role?.properties?.[columnName], ['compMod']);
69+
70+
result.push(
71+
generateSqlAlterScript({
72+
collectionSchema,
73+
prevJsonSchema: oldJsonSchema,
74+
jsonSchema,
75+
fullName,
76+
columnName,
77+
schemaName,
78+
}),
79+
);
80+
81+
return result;
82+
}, []),
83+
);
84+
};
85+
86+
return {
87+
getChangedComputedColumnsScriptsDto,
88+
};
89+
};

forward_engineering/helpers/general.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ module.exports = app => {
99

1010
const getTableName = (tableName, schemaName, brackets = true) => {
1111
const withBrackets = name => (brackets ? `[${name}]` : name);
12+
1213
if (schemaName) {
1314
return `${withBrackets(schemaName)}.${withBrackets(tableName)}`;
14-
} else {
15-
return withBrackets(tableName);
1615
}
16+
17+
return withBrackets(tableName);
1718
};
1819

1920
const getDefaultValue = (defaultValue, defaultConstraintName, type) => {

forward_engineering/utils/general.js

+5
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ module.exports = _ => {
158158
return `[${name}]`;
159159
};
160160

161+
const wrapInBracketsIfNecessary = name => {
162+
return name.replace(/^(?!\().*?(?<!\))$/, '($&)');
163+
};
164+
161165
const escapeSpecialCharacters = (name = '') => {
162166
return name.replace(/'/g, "''");
163167
};
@@ -216,6 +220,7 @@ module.exports = _ => {
216220
commentDeactivatedInlineKeys,
217221
buildScript,
218222
wrapInBrackets,
223+
wrapInBracketsIfNecessary,
219224
escapeSpecialCharacters,
220225
getFullEntityName,
221226
getFullTableName,

0 commit comments

Comments
 (0)