Skip to content

Commit a00882c

Browse files
authored
HCK-14284: [Alter script]: Implement CREATE/DROP logic for schemas (#73)
<!--do not remove this marker, its needed to replace info when ticket title is updated --> <!--jira-description-action-hidden-marker-start--> <table> <td> <a href="https://hackolade.atlassian.net/browse/HCK-14284" title="HCK-14284" target="_blank"><img alt="Sub-task" src="https://hackolade.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10316?size=medium" />HCK-14284</a> [DB2] Implement CREATE/DROP logic for schemas </td></table> <br /> <!--jira-description-action-hidden-marker-end--> ## Technical details - Added a new helper getUpdatedProperties to make it easier to support more changed properties in the future (for example, if we decide to add row_modification_tracking). - Refactored commentsHelper. I introduced new drop methods because the previous logic did not handle deletions. Reusing getCommentStatement with an empty description for delete operations would require adding checks in many existing places, so separating this logic makes the code safer and easier to maintain. Note: I also discovered that there are some general issues with comments in the plugin. As a result, this implementation might be revised further once those issues are addressed.
1 parent 7e980e8 commit a00882c

7 files changed

Lines changed: 251 additions & 13 deletions

File tree

forward_engineering/alterScript/alterScriptFromDeltaHelper.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const { getContainersScripts } = require('./alterScriptHelpers/alterContainerHelper');
12
const {
23
getModifyCollectionScriptDtos,
34
getModifyCollectionKeysScriptDtos,
@@ -12,6 +13,37 @@ const { getModifyViewScriptDtos } = require('./alterScriptHelpers/alterViewHelpe
1213

1314
const getItems = data => [data?.items].flat().filter(Boolean);
1415

16+
/**
17+
* @param dto {{
18+
* collection: Object,
19+
* app: App
20+
* }}
21+
* @return {AlterScriptDto[]}
22+
* */
23+
const getAlterContainersScriptDtos = ({ collection, app }) => {
24+
const { added, deleted, modified } = collection.properties?.containers?.properties || {};
25+
const addedContainers = getItems(added);
26+
const deletedContainers = getItems(deleted);
27+
const modifiedContainers = getItems(modified);
28+
29+
const { getAddContainerScriptDto, getDeleteContainerScriptDto, getModifyContainerScriptDto } =
30+
getContainersScripts(app);
31+
32+
const addContainersScriptDtos = addedContainers
33+
.map(container => Object.values(container.properties)[0])
34+
.flatMap(getAddContainerScriptDto);
35+
36+
const deleteContainersScriptDtos = deletedContainers
37+
.map(container => Object.values(container.properties)[0])
38+
.flatMap(getDeleteContainerScriptDto);
39+
40+
const modifyContainersScriptDtos = modifiedContainers
41+
.map(containerWrapper => Object.values(containerWrapper.properties)[0])
42+
.flatMap(getModifyContainerScriptDto);
43+
44+
return [...addContainersScriptDtos, ...deleteContainersScriptDtos, ...modifyContainersScriptDtos].filter(Boolean);
45+
};
46+
1547
const getAlterCollectionScriptDtos = ({
1648
collection,
1749
app,
@@ -127,6 +159,8 @@ const getAlterScriptDtos = (data, app) => {
127159
const inlineDeltaRelationships = getInlineRelationships({ collection, options: data.options });
128160
const ignoreRelationshipIDs = inlineDeltaRelationships.map(relationship => relationship.role.id);
129161

162+
const containersScriptDtos = getAlterContainersScriptDtos({ collection, app });
163+
130164
const collectionsScriptDtos = getAlterCollectionScriptDtos({
131165
collection,
132166
app,
@@ -143,7 +177,7 @@ const getAlterScriptDtos = (data, app) => {
143177
ignoreRelationshipIDs,
144178
});
145179

146-
return [...collectionsScriptDtos, ...viewScriptDtos, ...relationshipScriptDtos]
180+
return [...containersScriptDtos, ...collectionsScriptDtos, ...viewScriptDtos, ...relationshipScriptDtos]
147181
.filter(Boolean)
148182
.map(dto => dto && prettifyAlterScriptDto(dto))
149183
.filter(Boolean);
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
const { isEmpty } = require('lodash');
2+
const ddlProvider = require('../../ddlProvider');
3+
const { AlterScriptDto } = require('../types/AlterScriptDto');
4+
const {
5+
getSchemaCommentStatement,
6+
getDeleteCommentStatement,
7+
} = require('../../ddlProvider/ddlHelpers/comment/commentHelper');
8+
const { wrapInQuotes, getIsChangeProperties, getUpdatedProperties } = require('../../utils/general');
9+
const { getModifiedCommentOnSchemaScriptDtos } = require('./containerHelpers/commentsHelper');
10+
11+
const extractSchemaName = containerData => containerData.role.name;
12+
13+
const getAddContainerScriptDto = ddlProvider => containerData => {
14+
const schemaData = {
15+
...containerData.role,
16+
schemaName: extractSchemaName(containerData),
17+
};
18+
const script = ddlProvider.createSchema(schemaData);
19+
20+
return AlterScriptDto.getInstance([script], true, false);
21+
};
22+
23+
const getDeleteContainerScriptDto = ddlProvider => containerData => {
24+
const script = ddlProvider.dropSchema({ name: extractSchemaName(containerData) });
25+
26+
return AlterScriptDto.getInstance([script], true, true);
27+
};
28+
29+
const getModifyContainerScriptDto = ddlProvider => containerData => {
30+
const scripts = [];
31+
const compMod = containerData.role?.compMod || {};
32+
const schemaName = extractSchemaName(containerData);
33+
const wrappedSchemaName = wrapInQuotes(schemaName);
34+
const isActivated = containerData.isActivated !== false;
35+
const updatedProperties = getUpdatedProperties(compMod, ['dataCapture']);
36+
37+
if (!isEmpty(updatedProperties)) {
38+
const alterDataCaptureScript = ddlProvider.alterSchema(schemaName, updatedProperties);
39+
scripts.push(AlterScriptDto.getInstance([alterDataCaptureScript], isActivated, false));
40+
}
41+
42+
const commentScript = getModifiedCommentOnSchemaScriptDtos({
43+
schemaName: wrappedSchemaName,
44+
compMod,
45+
isActivated,
46+
});
47+
48+
if (commentScript) {
49+
scripts.push(commentScript);
50+
}
51+
52+
return scripts;
53+
};
54+
55+
const getContainersScripts = app => {
56+
const ddlProvider = require('../../ddlProvider/ddlProvider')(null, null, app);
57+
58+
return {
59+
getAddContainerScriptDto: getAddContainerScriptDto(ddlProvider),
60+
getDeleteContainerScriptDto: getDeleteContainerScriptDto(ddlProvider),
61+
getModifyContainerScriptDto: getModifyContainerScriptDto(ddlProvider),
62+
};
63+
};
64+
65+
module.exports = {
66+
getContainersScripts,
67+
};
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const { AlterScriptDto } = require('../../types/AlterScriptDto');
2+
const {
3+
getSchemaCommentStatement,
4+
dropSchemaCommentStatement,
5+
} = require('../../../ddlProvider/ddlHelpers/comment/commentHelper');
6+
const { wrapInQuotes } = require('../../../utils/general');
7+
8+
const getModifiedCommentOnSchemaScriptDtos = ({ schemaName, compMod, isActivated }) => {
9+
const description = compMod.description || {};
10+
11+
if (description.new && description.new !== description.old) {
12+
const script = getSchemaCommentStatement({ schemaName, description: description.new });
13+
return AlterScriptDto.getInstance([script], isActivated, false);
14+
}
15+
16+
if (description.old && !description.new) {
17+
const script = dropSchemaCommentStatement({ schemaName });
18+
return AlterScriptDto.getInstance([script], isActivated, true);
19+
}
20+
21+
return undefined;
22+
};
23+
24+
module.exports = {
25+
getModifiedCommentOnSchemaScriptDtos,
26+
};

forward_engineering/ddlProvider/ddlHelpers/comment/commentHelper.js

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,32 @@ const { wrapInQuotes, commentIfDeactivated, wrapInSingleQuotes } = require('../.
77
* @enum {string}
88
*/
99
const OBJECT_TYPE = {
10+
schema: 'SCHEMA',
1011
column: 'COLUMN',
1112
table: 'TABLE',
1213
index: 'INDEX',
1314
};
1415

16+
/**
17+
* @enum {string}
18+
*/
19+
const COMMENT_MODE = {
20+
set: 'set',
21+
remove: 'remove',
22+
};
23+
1524
/**
1625
* @param {string} description
1726
* @returns {string}
1827
*/
1928
const escapeSpecialCharacters = description => description.replace(/'/g, "''");
2029

2130
/**
22-
* @param {{ objectName: string, objectType: OBJECT_TYPE, description?: string }}
31+
* @param {{ objectName: string, objectType: OBJECT_TYPE, description?: string, mode?: COMMENT_MODE }}
2332
* @returns {string}
2433
*/
25-
const getCommentStatement = ({ objectName, objectType, description }) => {
26-
if (!description) {
34+
const getCommentStatement = ({ objectName, objectType, description, mode = COMMENT_MODE.set }) => {
35+
if (mode === COMMENT_MODE.set && !description) {
2736
return '';
2837
}
2938

@@ -32,34 +41,62 @@ const getCommentStatement = ({ objectName, objectType, description }) => {
3241
templateData: {
3342
objectType,
3443
objectName: trim(objectName),
35-
comment: wrapInSingleQuotes({ name: escapeSpecialCharacters(description) }),
44+
comment: wrapInSingleQuotes({ name: escapeSpecialCharacters(description || '') }),
3645
},
3746
});
3847
};
3948

4049
/**
41-
* @param {{ tableName, string, columnName: string, description?: string }}
50+
* @param {{ tableName, columnName: string, description?: string }}
4251
* @returns {string}
4352
*/
4453
const getColumnCommentStatement = ({ tableName, columnName, description }) => {
4554
const objectName = tableName + '.' + wrapInQuotes(columnName);
46-
return getCommentStatement({ objectName, objectType: OBJECT_TYPE.column, description });
55+
return getCommentStatement({
56+
objectName,
57+
objectType: OBJECT_TYPE.column,
58+
description,
59+
mode: COMMENT_MODE.set,
60+
});
4761
};
4862

4963
/**
5064
* @param {{ tableName: string, description?: string }}
5165
* @returns {string}
5266
*/
5367
const getTableCommentStatement = ({ tableName, description }) => {
54-
return getCommentStatement({ objectName: tableName, objectType: OBJECT_TYPE.table, description });
68+
return getCommentStatement({
69+
objectName: tableName,
70+
objectType: OBJECT_TYPE.table,
71+
description,
72+
mode: COMMENT_MODE.set,
73+
});
5574
};
5675

5776
/**
5877
* @param {{ indexName: string, description?: string }}
5978
* @returns {string}
6079
*/
6180
const getIndexCommentStatement = ({ indexName, description }) => {
62-
return getCommentStatement({ objectName: indexName, objectType: OBJECT_TYPE.index, description });
81+
return getCommentStatement({
82+
objectName: indexName,
83+
objectType: OBJECT_TYPE.index,
84+
description,
85+
mode: COMMENT_MODE.set,
86+
});
87+
};
88+
89+
/**
90+
* @param {{ schemaName: string, description?: string }}
91+
* @returns {string}
92+
*/
93+
const getSchemaCommentStatement = ({ schemaName, description }) => {
94+
return getCommentStatement({
95+
objectName: schemaName,
96+
objectType: OBJECT_TYPE.schema,
97+
description,
98+
mode: COMMENT_MODE.set,
99+
});
63100
};
64101

65102
/**
@@ -81,9 +118,24 @@ const getColumnComments = ({ tableName, columnDefinitions = [] }) => {
81118
.join('\n');
82119
};
83120

121+
/**
122+
* @param {{ schemaName: string }}
123+
* @returns {string}
124+
*/
125+
const dropSchemaCommentStatement = ({ schemaName }) => {
126+
return getCommentStatement({
127+
objectName: schemaName,
128+
objectType: OBJECT_TYPE.schema,
129+
mode: COMMENT_MODE.remove,
130+
});
131+
};
132+
84133
module.exports = {
85134
getColumnCommentStatement,
135+
getSchemaCommentStatement,
86136
getTableCommentStatement,
87137
getColumnComments,
88138
getIndexCommentStatement,
139+
140+
dropSchemaCommentStatement,
89141
};

forward_engineering/ddlProvider/ddlProvider.js

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const {
2020
getTableCommentStatement,
2121
getColumnComments,
2222
getIndexCommentStatement,
23+
getSchemaCommentStatement,
2324
} = require('./ddlHelpers/comment/commentHelper.js');
2425
const { getTableProps } = require('./ddlHelpers/table/getTableProps.js');
2526
const { getTableOptions } = require('./ddlHelpers/table/getTableOptions.js');
@@ -65,20 +66,46 @@ module.exports = (baseProvider, options, app) => {
6566
authorizationName: containerData.authorizationName,
6667
dataCapture: containerData.dataCapture,
6768
isActivated: containerData.isActivated,
69+
description: containerData.description,
6870
};
6971
},
7072

71-
createSchema({ schemaName, ifNotExist, authorizationName, dataCapture, isActivated = true }) {
73+
createSchema({ schemaName, ifNotExist, authorizationName, dataCapture, description, isActivated = true }) {
74+
const wrappedSchemaName = wrapInQuotes(schemaName);
7275
const schemaStatement = assignTemplates({
7376
template: templates.createSchema,
7477
templateData: {
75-
schemaName: wrapInQuotes(schemaName),
78+
schemaName: wrappedSchemaName,
7679
authorization: authorizationName ? ' AUTHORIZATION ' + authorizationName : '',
7780
dataCapture: dataCapture ? ' DATA CAPTURE ' + dataCapture : '',
7881
},
7982
});
8083

81-
return commentIfDeactivated(schemaStatement, { isActivated });
84+
const comment = getSchemaCommentStatement({ schemaName: wrappedSchemaName, description });
85+
const commentStatement = comment ? '\n' + comment + '\n' : '\n';
86+
87+
return commentIfDeactivated(schemaStatement + commentStatement, { isActivated });
88+
},
89+
90+
dropSchema({ name, isActivated = true }) {
91+
const dropSchemaStatement = assignTemplates({
92+
template: templates.dropSchema,
93+
templateData: {
94+
schemaName: wrapInQuotes(name),
95+
},
96+
});
97+
98+
return commentIfDeactivated(dropSchemaStatement, { isActivated });
99+
},
100+
101+
alterSchema(schemaName, { dataCapture }) {
102+
return assignTemplates({
103+
template: templates.alterSchema,
104+
templateData: {
105+
schemaName: wrapInQuotes(schemaName),
106+
dataCapture: dataCapture ? ' DATA CAPTURE ' + dataCapture : '',
107+
},
108+
});
82109
},
83110

84111
hydrateColumn({ columnDefinition, jsonSchema, schemaData, definitionJsonSchema = {} }) {

forward_engineering/ddlProvider/templates.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
module.exports = {
22
createSchema: 'CREATE SCHEMA ${schemaName}${authorization}${dataCapture};',
33

4+
dropSchema: 'DROP SCHEMA ${schemaName} RESTRICT',
5+
6+
alterSchema: 'ALTER SCHEMA ${schemaName}${dataCapture};',
7+
48
createTable: 'CREATE${tableType} TABLE${ifNotExists} ${name}${tableProps}${tableOptions};',
59

610
createAuxiliaryTable: 'CREATE${tableType} TABLE ${name}${tableOptions};',

forward_engineering/utils/general.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { toLower, omit } = require('lodash');
1+
const { toLower, omit, isEqual } = require('lodash');
22
const { INLINE_COMMENT } = require('../../constants/constants');
33

44
/**
@@ -146,6 +146,33 @@ const isParentContainerActivated = collection => {
146146
);
147147
};
148148

149+
/**
150+
*
151+
* @template {object} T
152+
* @param {{ new: T, old: T }}
153+
* @returns {boolean}
154+
*/
155+
const compareProperties = ({ new: newProperty, old: oldProperty }) => {
156+
if (!newProperty && !oldProperty) {
157+
return;
158+
}
159+
return !isEqual(newProperty, oldProperty);
160+
};
161+
162+
/**
163+
* @param {object} compMod
164+
* @param {string[]} properties
165+
* @returns {object} Only changed properties with their new values
166+
*/
167+
const getUpdatedProperties = (compMod, properties) =>
168+
properties.reduce((acc, property) => {
169+
const propCompMod = compMod[property] || {};
170+
if (compareProperties(propCompMod) && propCompMod.new !== undefined) {
171+
acc[property] = propCompMod.new;
172+
}
173+
return acc;
174+
}, {});
175+
149176
module.exports = {
150177
setTab,
151178
hasType,
@@ -165,4 +192,5 @@ module.exports = {
165192
isObjectInDeltaModelActivated,
166193
isParentContainerActivated,
167194
getSchemaNameFromCollection,
195+
getUpdatedProperties,
168196
};

0 commit comments

Comments
 (0)