From 61666516206c4b35d6336959f139b6289730e627 Mon Sep 17 00:00:00 2001 From: yevhenii moroziuk Date: Mon, 28 Jul 2025 12:52:02 +0300 Subject: [PATCH 1/2] HCK-12142: Add support of alter default value --- forward_engineering/configs/templates.js | 2 +- forward_engineering/ddlProvider.js | 22 ++--- .../alterScriptHelpers/alterEntityHelper.js | 3 + .../columnHelpers/defaultValueColumnHelper.js | 96 +++++++++++++++++++ forward_engineering/helpers/general.js | 36 +++---- 5 files changed, 121 insertions(+), 38 deletions(-) create mode 100644 forward_engineering/helpers/alterScriptHelpers/columnHelpers/defaultValueColumnHelper.js diff --git a/forward_engineering/configs/templates.js b/forward_engineering/configs/templates.js index 8d336af6..edff7e32 100644 --- a/forward_engineering/configs/templates.js +++ b/forward_engineering/configs/templates.js @@ -11,7 +11,7 @@ module.exports = { ')${options}${terminator}\n${comment}${columnComments}', columnDefinition: - '[${name}] ${type}${primary_key}${temporalTableTime}${sparse}${maskedWithFunction}${identity}${default}${collation}${not_null}${encryptedWith}', + '[${name}] ${type}${primary_key}${temporalTableTime}${sparse}${maskedWithFunction}${identity}${collation}${not_null}${default}${encryptedWith}', computedColumnDefinition: '[${name}] AS ${expression}${persisted}${key}${not_null}', index: diff --git a/forward_engineering/ddlProvider.js b/forward_engineering/ddlProvider.js index 257a8cd2..74f21af2 100644 --- a/forward_engineering/ddlProvider.js +++ b/forward_engineering/ddlProvider.js @@ -25,7 +25,6 @@ module.exports = (baseProvider, options, app) => { getTableName, getTableOptions, hasType, - getDefaultConstraints, foreignKeysToString, checkIndexActivated, getDefaultValue, @@ -99,6 +98,10 @@ module.exports = (baseProvider, options, app) => { return databaseStatement + '\n\n' + useStatement + '\n\n' + schemaStatement; }, + createDefaultConstraint(data, tableName, terminator = ';') { + return createDefaultConstraint(templates, terminator)(data, tableName); + }, + createTable( { name, @@ -161,20 +164,15 @@ module.exports = (baseProvider, options, app) => { comment: tableComment ? `\n${tableComment}` : '', columnComments: columnComments ? `${tableAndColumnCommentsSeparator}${columnComments}\n` : '', }); - const defaultConstraintsStatements = defaultConstraints - .map(data => createDefaultConstraint(templates, tableTerminator)(data, tableName)) - .join('\n'); - - const fullTableStatement = [tableStatement, defaultConstraintsStatements].filter(Boolean).join('\n\n'); return ifNotExist ? wrapIfNotExistTable({ - tableStatement: fullTableStatement, + tableStatement, templates, tableName: getTableName(name, schemaData.schemaName, false), terminator, }) - : fullTableStatement; + : tableStatement; }, createComputedColumn({ name, computedExpression, persisted, primaryKey, unique, notNull }) { @@ -201,11 +199,7 @@ module.exports = (baseProvider, options, app) => { const primaryKey = columnDefinition.primaryKey ? ' ' + createPKConstraint(templates, terminator, true)(columnDefinition.primaryKeyOptions).statement : ''; - const defaultValue = getDefaultValue( - columnDefinition.default, - columnDefinition.defaultConstraint?.name, - type, - ); + const defaultValue = getDefaultValue(columnDefinition.defaultConstraint, type); const sparse = columnDefinition.sparse ? ' SPARSE' : ''; const maskedWithFunction = columnDefinition.maskedWithFunction ? ` MASKED WITH (FUNCTION='${columnDefinition.maskedWithFunction}')` @@ -464,7 +458,6 @@ module.exports = (baseProvider, options, app) => { _.get(parentJsonSchema, 'periodForSystemTime[0].startTime[0].type', '') === 'hidden'; return Object.assign({}, columnDefinition, { - default: jsonSchema.defaultConstraintName ? '' : columnDefinition.default, defaultConstraint: { name: jsonSchema.defaultConstraintName, value: columnDefinition.default, @@ -547,7 +540,6 @@ module.exports = (baseProvider, options, app) => { return Object.assign({}, tableData, { foreignKeyConstraints: tableData.foreignKeyConstraints || [], keyConstraints: keyHelper.getTableKeyConstraints({ jsonSchema }), - defaultConstraints: getDefaultConstraints(tableData.columnDefinitions), ifNotExist: jsonSchema.ifNotExist, comment: jsonSchema.description, columnDefinitions: tableData.columnDefinitions, diff --git a/forward_engineering/helpers/alterScriptHelpers/alterEntityHelper.js b/forward_engineering/helpers/alterScriptHelpers/alterEntityHelper.js index c5c6b9c9..c7c6f186 100644 --- a/forward_engineering/helpers/alterScriptHelpers/alterEntityHelper.js +++ b/forward_engineering/helpers/alterScriptHelpers/alterEntityHelper.js @@ -9,6 +9,7 @@ module.exports = (app, options) => { const { generateIdToNameHashTable, generateIdToActivatedHashTable } = app.require('@hackolade/ddl-fe-utils'); const { setIndexKeys, modifyGroupItems } = require('./common')(app); const { getRenameColumnScriptsDto } = require('./columnHelpers/renameColumnHelpers')(app, ddlProvider); + const { getDefaultValueChangeDto } = require('./columnHelpers/defaultValueColumnHelper')(app, ddlProvider); const { getChangedComputedColumnsScriptsDto } = require('./columnHelpers/alterComputedColumnHelpr')( app, ddlProvider, @@ -199,6 +200,7 @@ module.exports = (app, options) => { collectionSchema, schemaName, ); + const modifiedDefaultValues = getDefaultValueChangeDto(collection, fullName); const changedComputedScriptsDtos = getChangedComputedColumnsScriptsDto({ collection, fullName, @@ -210,6 +212,7 @@ module.exports = (app, options) => { ...renameColumnScriptsDtos, ...changeTypeScriptsDtos, ...modifyNotNullScriptDtos, + ...modifiedDefaultValues, ...changedComputedScriptsDtos, ].filter(Boolean); }; diff --git a/forward_engineering/helpers/alterScriptHelpers/columnHelpers/defaultValueColumnHelper.js b/forward_engineering/helpers/alterScriptHelpers/columnHelpers/defaultValueColumnHelper.js new file mode 100644 index 00000000..eaa78adc --- /dev/null +++ b/forward_engineering/helpers/alterScriptHelpers/columnHelpers/defaultValueColumnHelper.js @@ -0,0 +1,96 @@ +module.exports = (app, ddlProvider) => { + const { AlterScriptDto } = require('../types/AlterScriptDto'); + const { sanitizeConstraintName } = require('../../../helpers/general')(app); + + const getDefaultValueChangeDto = (collection, fullName) => { + const scripts = []; + + const getDefaultConstraintName = columnName => sanitizeConstraintName(`DF_${fullName}_${columnName}`); + + Object.entries(collection.properties).forEach(([columnName, collectionSchema]) => { + const newDefaultValue = collectionSchema.default; + const newConstraintName = collectionSchema.defaultConstraintName; + const oldDefaultValue = collection.role.properties[columnName]?.default; + const oldConstraintName = collection.role.properties[columnName]?.defaultConstraintName; + + const defaultValueWasRemoved = !!oldDefaultValue && !newDefaultValue; + const defaultValueWasAdded = !oldDefaultValue && !!newDefaultValue; + const defaultValueWasChanged = + !!oldDefaultValue && !!newDefaultValue && oldDefaultValue !== newDefaultValue; + const constraintNameChanged = + !!oldDefaultValue && + !!newDefaultValue && + oldDefaultValue === newDefaultValue && + !!oldConstraintName && + !!newConstraintName && + oldConstraintName !== newConstraintName; + + switch (true) { + case defaultValueWasRemoved: { + if (oldConstraintName) { + const dropScript = ddlProvider.dropConstraint(fullName, oldConstraintName); + scripts.push(AlterScriptDto.getInstance([dropScript], true, true)); + } + break; + } + case defaultValueWasAdded: { + const constraintName = newConstraintName || getDefaultConstraintName(columnName); + + const createScript = ddlProvider.createDefaultConstraint( + { + constraintName, + columnName, + value: newDefaultValue, + }, + fullName, + ); + scripts.push(AlterScriptDto.getInstance([createScript], true, false)); + break; + } + case defaultValueWasChanged: { + if (oldConstraintName) { + const dropScript = ddlProvider.dropConstraint(fullName, oldConstraintName); + scripts.push(AlterScriptDto.getInstance([dropScript], true, true)); + } + const constraintName = newConstraintName || getDefaultConstraintName(columnName); + + const createScript = ddlProvider.createDefaultConstraint( + { + constraintName, + columnName, + value: newDefaultValue, + }, + fullName, + ); + scripts.push(AlterScriptDto.getInstance([createScript], true, false)); + break; + } + case constraintNameChanged: { + const dropScript = ddlProvider.dropConstraint(fullName, oldConstraintName); + + const createScript = ddlProvider.createDefaultConstraint( + { + constraintName: newConstraintName, + columnName, + value: newDefaultValue, + }, + fullName, + ); + scripts.push( + AlterScriptDto.getInstance([dropScript], true, true), + AlterScriptDto.getInstance([createScript], true, false), + ); + break; + } + default: + break; + } + }); + + return scripts; + }; + + return { + getDefaultValueChangeDto, + }; +}; diff --git a/forward_engineering/helpers/general.js b/forward_engineering/helpers/general.js index 063399a1..13a4d9e8 100644 --- a/forward_engineering/helpers/general.js +++ b/forward_engineering/helpers/general.js @@ -17,14 +17,22 @@ module.exports = app => { return withBrackets(tableName); }; - const getDefaultValue = (defaultValue, defaultConstraintName, type) => { - if (_.isUndefined(defaultValue)) { + const getDefaultValue = (defaultConstraint, type) => { + if (_.isUndefined(defaultConstraint.value)) { return ''; } - if (!_.isUndefined(defaultConstraintName)) { - return ''; + + const value = decorateDefault(type, defaultConstraint.value); + + if (!_.isUndefined(defaultConstraint.name)) { + return ` CONSTRAINT ${defaultConstraint.name} DEFAULT ${value}`; } - return ` DEFAULT ${decorateDefault(type, defaultValue)}`; + + return ` DEFAULT ${value}`; + }; + + const sanitizeConstraintName = str => { + return str ? str.replace(/\[|\]/g, '').replace(/\./g, '_') : str; }; const getKeyWithAlias = key => { @@ -189,22 +197,6 @@ module.exports = app => { }, {}); }; - const getDefaultConstraints = columnDefinitions => { - if (!Array.isArray(columnDefinitions)) { - return []; - } - - return columnDefinitions - .filter( - column => _.get(column, 'defaultConstraint.name') && !_.isNil(_.get(column, 'defaultConstraint.value')), - ) - .map(column => ({ - columnName: column.name, - constraintName: column.defaultConstraint.name, - value: decorateDefault(column.type, column.defaultConstraint.value), - })); - }; - const foreignKeysToString = keys => { if (Array.isArray(keys)) { const activatedKeys = keys @@ -266,10 +258,10 @@ module.exports = app => { getTableOptions, hasType, getViewData, - getDefaultConstraints, foreignKeysToString, additionalPropertiesForForeignKey, trimBraces, + sanitizeConstraintName, checkIndexActivated, foreignActiveKeysToString, getDefaultValue, From caf89f40d8147653197f6014d28f451c79cd9ca7 Mon Sep 17 00:00:00 2001 From: yevhenii moroziuk Date: Mon, 28 Jul 2025 13:48:01 +0300 Subject: [PATCH 2/2] HCK-12142: Safely access collection properties --- .../columnHelpers/defaultValueColumnHelper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forward_engineering/helpers/alterScriptHelpers/columnHelpers/defaultValueColumnHelper.js b/forward_engineering/helpers/alterScriptHelpers/columnHelpers/defaultValueColumnHelper.js index eaa78adc..09a87aea 100644 --- a/forward_engineering/helpers/alterScriptHelpers/columnHelpers/defaultValueColumnHelper.js +++ b/forward_engineering/helpers/alterScriptHelpers/columnHelpers/defaultValueColumnHelper.js @@ -7,7 +7,7 @@ module.exports = (app, ddlProvider) => { const getDefaultConstraintName = columnName => sanitizeConstraintName(`DF_${fullName}_${columnName}`); - Object.entries(collection.properties).forEach(([columnName, collectionSchema]) => { + Object.entries(collection?.properties ?? []).forEach(([columnName, collectionSchema]) => { const newDefaultValue = collectionSchema.default; const newConstraintName = collectionSchema.defaultConstraintName; const oldDefaultValue = collection.role.properties[columnName]?.default;