From 2d94315c75204497e582c848a85317949dbb3a15 Mon Sep 17 00:00:00 2001 From: Taras Dubyk Date: Thu, 6 Nov 2025 13:24:07 +0200 Subject: [PATCH 1/5] add pk, uk script options --- .../helpers/alterScriptFromDeltaHelper.js | 8 + .../alterScriptHelpers/alterEntityHelper.js | 11 + .../entityHelper/primaryKeyHelper.js | 28 ++ .../entityHelper/sharedKeyConstraintHelper.js | 331 ++++++++++++++++++ .../entityHelper/uniqueKeyHelper.js | 28 ++ 5 files changed, 406 insertions(+) create mode 100644 forward_engineering/helpers/alterScriptHelpers/entityHelper/primaryKeyHelper.js create mode 100644 forward_engineering/helpers/alterScriptHelpers/entityHelper/sharedKeyConstraintHelper.js create mode 100644 forward_engineering/helpers/alterScriptHelpers/entityHelper/uniqueKeyHelper.js diff --git a/forward_engineering/helpers/alterScriptFromDeltaHelper.js b/forward_engineering/helpers/alterScriptFromDeltaHelper.js index 46110e4..10dfcd8 100644 --- a/forward_engineering/helpers/alterScriptFromDeltaHelper.js +++ b/forward_engineering/helpers/alterScriptFromDeltaHelper.js @@ -29,6 +29,7 @@ const getAlterCollectionsScripts = (collection, app, options) => { getDeleteColumnScript, getModifyColumnScript, getModifyCollectionScript, + getModifyCollectionKeysScript, } = require('./alterScriptHelpers/alterEntityHelper')(app, options); const createCollectionsScripts = [collection.properties?.entities?.properties?.added?.items] @@ -72,6 +73,12 @@ const getAlterCollectionsScripts = (collection, app, options) => { .map(item => Object.values(item.properties)[0]) .flatMap(getModifyColumnScript); + const modifyCollectionKeysScripts = [collection.properties?.entities?.properties?.modified?.items] + .flat() + .filter(Boolean) + .map(item => Object.values(item.properties)[0]) + .flatMap(getModifyCollectionKeysScript); + return [ ...createCollectionsScripts, ...deleteCollectionScripts, @@ -79,6 +86,7 @@ const getAlterCollectionsScripts = (collection, app, options) => { ...modifyCollectionScripts, ...deleteColumnScripts, ...modifyColumnScript, + ...modifyCollectionKeysScripts, ] .filter(Boolean) .map(script => script.trim()); diff --git a/forward_engineering/helpers/alterScriptHelpers/alterEntityHelper.js b/forward_engineering/helpers/alterScriptHelpers/alterEntityHelper.js index b458109..8b9c197 100644 --- a/forward_engineering/helpers/alterScriptHelpers/alterEntityHelper.js +++ b/forward_engineering/helpers/alterScriptHelpers/alterEntityHelper.js @@ -3,6 +3,8 @@ const { getTableName } = require('../general'); const { getEntityName } = require('../../utils/general'); const { createColumnDefinitionBySchema } = require('./createColumnDefinition'); const { checkFieldPropertiesChanged, modifyGroupItems, setIndexKeys } = require('./common'); +const { getModifyPkScripts } = require('./entityHelper/primaryKeyHelper'); +const { getModifyUkScripts } = require('./entityHelper/uniqueKeyHelper'); const alterEntityHelper = (app, options) => { const ddlProvider = require('../../ddlProvider')(null, options, app); @@ -121,6 +123,7 @@ const alterEntityHelper = (app, options) => { }; const getModifyColumnScript = collection => { + // TODO: entrypoint const collectionSchema = { ...collection, ..._.omit(collection?.role, 'properties') }; const tableName = collectionSchema?.code || collectionSchema?.collectionName || collectionSchema?.name; const schemaName = collectionSchema.compMod?.keyspaceName; @@ -150,6 +153,13 @@ const alterEntityHelper = (app, options) => { return [...renameColumnScripts, ...changeTypeScripts]; }; + const getModifyCollectionKeysScript = collection => { + const modifyPkScripts = getModifyPkScripts(collection, options); + const modifyUkScripts = getModifyUkScripts(collection, options); + + return [...modifyPkScripts, ...modifyUkScripts].filter(Boolean); + }; + const hydrateIndex = ({ idToNameHashTable, idToActivatedHashTable, ddlProvider, tableData, schemaData }) => index => { @@ -165,6 +175,7 @@ const alterEntityHelper = (app, options) => { getAddColumnScript, getDeleteColumnScript, getModifyColumnScript, + getModifyCollectionKeysScript, }; }; diff --git a/forward_engineering/helpers/alterScriptHelpers/entityHelper/primaryKeyHelper.js b/forward_engineering/helpers/alterScriptHelpers/entityHelper/primaryKeyHelper.js new file mode 100644 index 0000000..f1ab8a3 --- /dev/null +++ b/forward_engineering/helpers/alterScriptHelpers/entityHelper/primaryKeyHelper.js @@ -0,0 +1,28 @@ +const { getModifyKeyScripts } = require('./sharedKeyConstraintHelper'); + +/** + * Primary Key constraint configuration + */ +const PRIMARY_KEY_CONFIG = { + constraintType: 'PRIMARY KEY', + compModKeyName: 'primaryKey', + columnKeyProperty: 'primaryKey', + compositeKeyProperty: 'compositePrimaryKey', + constraintNameProperty: 'primaryKeyConstraintName', + optionsProperty: 'primaryKeyOptions', +}; + +/** + * Get all modify PK scripts (both composite and regular) + * + * @param {Object} collection + * @param {Object} options + * @return {string[]} + */ +const getModifyPkScripts = (collection, options) => { + return getModifyKeyScripts(collection, PRIMARY_KEY_CONFIG, options); +}; + +module.exports = { + getModifyPkScripts, +}; diff --git a/forward_engineering/helpers/alterScriptHelpers/entityHelper/sharedKeyConstraintHelper.js b/forward_engineering/helpers/alterScriptHelpers/entityHelper/sharedKeyConstraintHelper.js new file mode 100644 index 0000000..9fb8494 --- /dev/null +++ b/forward_engineering/helpers/alterScriptHelpers/entityHelper/sharedKeyConstraintHelper.js @@ -0,0 +1,331 @@ +const _ = require('lodash'); +const { getTableName } = require('../../general'); +const { getEntityName } = require('../../../utils/general'); +const { assignTemplates } = require('../../../utils/assignTemplates'); +const templates = require('../../../configs/templates'); +const { getTerminator } = require('../../optionsHelper'); + +const amountOfColumnsInRegularKey = 1; + +/** + * Configuration object for key constraint handling + * @typedef {Object} KeyConstraintConfig + * @property {string} constraintType - e.g., 'PRIMARY KEY' or 'UNIQUE' + * @property {string} compModKeyName - e.g., 'primaryKey' or 'uniqueKey' + * @property {string} columnKeyProperty - e.g., 'primaryKey' or 'unique' + * @property {string} compositeKeyProperty - e.g., 'compositePrimaryKey' or 'compositeUniqueKey' + * @property {string} constraintNameProperty - e.g., 'primaryKeyConstraintName' or 'uniqueKeyConstraintName' + * @property {string} optionsProperty - e.g., 'primaryKeyOptions' or 'uniqueKeyOptions' + */ + +/** + * Get column names from composite key by matching keyId with column GUID + * @param {Array<{ type: string, keyId: string}>} compositeKey + * @param {Object.} properties + * @param {KeyConstraintConfig} config + */ +const getCompositeKeyColumnNames = (compositeKey, properties, config) => { + return compositeKey + .map(keyDto => { + const column = Object.entries(properties).find(([name, col]) => col.GUID === keyDto.keyId); + return column ? { name: column[0], isActivated: column[1].isActivated } : null; + }) + .filter(Boolean); +}; + +/** + * Compare constraint details + * @param {Object} oldConstraint + * @param {Object} newConstraint + * @return {boolean} + */ +const areConstraintsEqual = (oldConstraint, newConstraint) => { + return _.isEqual(oldConstraint, newConstraint); +}; + +/** + * Get ADD CONSTRAINT scripts for composite keys + * @param {Object} collection + * @param {KeyConstraintConfig} config + * @param {Object} options + * @return {string[]} + */ +const getAddCompositeKeyScripts = (collection, config, options) => { + const terminator = getTerminator(options); + const keyDto = collection?.role?.compMod?.[config.compModKeyName] || {}; + const newKeys = keyDto.new || []; + const oldKeys = keyDto.old || []; + + if (newKeys.length === 0 && oldKeys.length === 0) { + return []; + } + + if (newKeys.length === oldKeys.length) { + const areKeyArraysEqual = _(oldKeys).differenceWith(newKeys, _.isEqual).isEmpty(); + if (areKeyArraysEqual) { + return []; + } + } + + const collectionSchema = { ...collection, ..._.omit(collection?.role, 'properties') }; + const tableName = getEntityName(collectionSchema); + const schemaName = collection.compMod?.keyspaceName; + const fullName = getTableName(tableName, schemaName); + + return newKeys + .map(newKey => { + const columns = getCompositeKeyColumnNames( + newKey[config.compositeKeyProperty] || [], + collection.role.properties, + config, + ); + + if (_.isEmpty(columns)) { + return null; + } + + const columnsStr = columns.map(col => `[${col.name}]`).join(', '); + const constraintName = newKey.constraintName ? `[${newKey.constraintName}]` : ''; + + const statement = constraintName + ? `CONSTRAINT ${constraintName} ${config.constraintType} NONCLUSTERED (${columnsStr}) NOT ENFORCED` + : `${config.constraintType} NONCLUSTERED (${columnsStr}) NOT ENFORCED`; + + return assignTemplates(templates.alterTableAddConstraint, { + tableName: fullName, + constraint: statement, + terminator, + }); + }) + .filter(Boolean); +}; + +/** + * Get DROP CONSTRAINT scripts for composite keys + * @param {Object} collection + * @param {KeyConstraintConfig} config + * @param {Object} options + * @return {string[]} + */ +const getDropCompositeKeyScripts = (collection, config, options) => { + const terminator = getTerminator(options); + const keyDto = collection?.role?.compMod?.[config.compModKeyName] || {}; + const newKeys = keyDto.new || []; + const oldKeys = keyDto.old || []; + + if (newKeys.length === 0 && oldKeys.length === 0) { + return []; + } + + if (newKeys.length === oldKeys.length) { + const areKeyArraysEqual = _(oldKeys).differenceWith(newKeys, _.isEqual).isEmpty(); + if (areKeyArraysEqual) { + return []; + } + } + + const collectionSchema = { ...collection, ..._.omit(collection?.role, 'properties') }; + const tableName = getEntityName(collectionSchema); + const schemaName = collection.compMod?.keyspaceName; + const fullName = getTableName(tableName, schemaName); + + return oldKeys + .map(oldKey => { + const constraintName = oldKey.constraintName; + if (!constraintName) { + return null; + } + + return assignTemplates(templates.alterTable, { + tableName: fullName, + command: `DROP CONSTRAINT [${constraintName}]`, + terminator, + }); + }) + .filter(Boolean); +}; + +/** + * Get modify scripts for composite keys (drop + add) + * @param {Object} collection + * @param {KeyConstraintConfig} config + * @param {Object} options + * @return {string[]} + */ +const getModifyCompositeKeyScripts = (collection, config, options) => { + const dropCompositeKeyScripts = getDropCompositeKeyScripts(collection, config, options); + const addCompositeKeyScripts = getAddCompositeKeyScripts(collection, config, options); + return [...dropCompositeKeyScripts, ...addCompositeKeyScripts].filter(Boolean); +}; + +/** + * Check if field was changed to be a regular key + * @param {Object} columnJsonSchema + * @param {Object} collection + * @param {KeyConstraintConfig} config + * @return {boolean} + */ +const wasFieldChangedToBeARegularKey = (columnJsonSchema, collection, config) => { + const oldName = columnJsonSchema.compMod.oldField.name; + const oldColumnJsonSchema = collection.role.properties[oldName]; + + const isRegularKey = columnJsonSchema[config.columnKeyProperty] && !columnJsonSchema[config.compositeKeyProperty]; + const wasTheFieldAnyKey = Boolean(oldColumnJsonSchema?.[config.columnKeyProperty]); + + return isRegularKey && !wasTheFieldAnyKey; +}; + +/** + * Check if field is no longer a regular key + * @param {Object} columnJsonSchema + * @param {Object} collection + * @param {KeyConstraintConfig} config + * @return {boolean} + */ +const isFieldNoLongerARegularKey = (columnJsonSchema, collection, config) => { + const oldName = columnJsonSchema.compMod.oldField.name; + const oldJsonSchema = collection.role.properties[oldName]; + const wasTheFieldARegularKey = + oldJsonSchema?.[config.columnKeyProperty] && !oldJsonSchema?.[config.compositeKeyProperty]; + + const isNotAnyKey = !columnJsonSchema[config.columnKeyProperty] && !columnJsonSchema[config.compositeKeyProperty]; + return wasTheFieldARegularKey && isNotAnyKey; +}; + +/** + * Check if regular key was modified + * @param {Object} columnJsonSchema + * @param {Object} collection + * @param {KeyConstraintConfig} config + * @return {boolean} + */ +const wasRegularKeyModified = (columnJsonSchema, collection, config) => { + const oldName = columnJsonSchema.compMod.oldField.name; + const oldJsonSchema = collection.role.properties[oldName] || {}; + + const isRegularKey = columnJsonSchema[config.columnKeyProperty] && !columnJsonSchema[config.compositeKeyProperty]; + const wasTheFieldARegularKey = + oldJsonSchema?.[config.columnKeyProperty] && !oldJsonSchema?.[config.compositeKeyProperty]; + + if (!(isRegularKey && wasTheFieldARegularKey)) { + return false; + } + + const oldOptions = _.get(oldJsonSchema, config.optionsProperty, [{}])[0] || {}; + const newOptions = _.get(columnJsonSchema, config.optionsProperty, [{}])[0] || {}; + + const oldConstraintName = oldOptions.constraintName || ''; + const newConstraintName = newOptions.constraintName || ''; + + return !areConstraintsEqual({ constraintName: oldConstraintName }, { constraintName: newConstraintName }); +}; + +/** + * Get ADD CONSTRAINT scripts for regular (column-level) keys + * @param {Object} collection + * @param {KeyConstraintConfig} config + * @param {Object} options + * @return {string[]} + */ +const getAddRegularKeyScripts = (collection, config, options) => { + const terminator = getTerminator(options); + const collectionSchema = { ...collection, ..._.omit(collection?.role, 'properties') }; + const tableName = getEntityName(collectionSchema); + const schemaName = collection.compMod?.keyspaceName; + const fullName = getTableName(tableName, schemaName); + + return _.toPairs(collection.properties) + .filter(([name, jsonSchema]) => { + if (wasFieldChangedToBeARegularKey(jsonSchema, collection, config)) { + return true; + } + return wasRegularKeyModified(jsonSchema, collection, config); + }) + .map(([name, jsonSchema]) => { + const options = _.get(jsonSchema, config.optionsProperty, [{}])[0] || {}; + const constraintName = options.constraintName ? `[${options.constraintName}]` : ''; + + const statement = constraintName + ? `CONSTRAINT ${constraintName} ${config.constraintType} NONCLUSTERED ([${name}]) NOT ENFORCED` + : `${config.constraintType} NONCLUSTERED ([${name}]) NOT ENFORCED`; + + return assignTemplates(templates.alterTableAddConstraint, { + tableName: fullName, + constraint: statement, + terminator, + }); + }) + .filter(Boolean); +}; + +/** + * Get DROP CONSTRAINT scripts for regular (column-level) keys + * @param {Object} collection + * @param {KeyConstraintConfig} config + * @param {Object} options + * @return {string[]} + */ +const getDropRegularKeyScripts = (collection, config, options) => { + const terminator = getTerminator(options); + const collectionSchema = { ...collection, ..._.omit(collection?.role, 'properties') }; + const tableName = getEntityName(collectionSchema); + const schemaName = collection.compMod?.keyspaceName; + const fullName = getTableName(tableName, schemaName); + + return _.toPairs(collection.properties) + .filter(([name, jsonSchema]) => { + if (isFieldNoLongerARegularKey(jsonSchema, collection, config)) { + return true; + } + return wasRegularKeyModified(jsonSchema, collection, config); + }) + .map(([name, jsonSchema]) => { + const oldName = jsonSchema.compMod.oldField.name; + const oldJsonSchema = collection.role.properties[oldName]; + const oldOptions = _.get(oldJsonSchema, config.optionsProperty, [{}])[0] || {}; + const constraintName = oldOptions.constraintName; + + if (!constraintName) { + return null; + } + + return assignTemplates(templates.alterTable, { + tableName: fullName, + command: `DROP CONSTRAINT [${constraintName}]`, + terminator, + }); + }) + .filter(Boolean); +}; + +/** + * Get modify scripts for regular keys (drop + add) + * @param {Object} collection + * @param {KeyConstraintConfig} config + * @param {Object} options + * @return {string[]} + */ +const getModifyRegularKeyScripts = (collection, config, options) => { + const dropKeyScripts = getDropRegularKeyScripts(collection, config, options); + const addKeyScripts = getAddRegularKeyScripts(collection, config, options); + return [...dropKeyScripts, ...addKeyScripts].filter(Boolean); +}; + +/** + * Get all modify key scripts (both composite and regular) + * + * @param {Object} collection + * @param {KeyConstraintConfig} config + * @param {Object} options + * @return {string[]} + */ +const getModifyKeyScripts = (collection, config, options) => { + const modifyCompositeKeyScripts = getModifyCompositeKeyScripts(collection, config, options); + const modifyRegularKeyScripts = getModifyRegularKeyScripts(collection, config, options); + + return [...modifyCompositeKeyScripts, ...modifyRegularKeyScripts].filter(Boolean); +}; + +module.exports = { + getModifyKeyScripts, +}; diff --git a/forward_engineering/helpers/alterScriptHelpers/entityHelper/uniqueKeyHelper.js b/forward_engineering/helpers/alterScriptHelpers/entityHelper/uniqueKeyHelper.js new file mode 100644 index 0000000..af178b3 --- /dev/null +++ b/forward_engineering/helpers/alterScriptHelpers/entityHelper/uniqueKeyHelper.js @@ -0,0 +1,28 @@ +const { getModifyKeyScripts } = require('./sharedKeyConstraintHelper'); + +/** + * Unique Key constraint configuration + */ +const UNIQUE_KEY_CONFIG = { + constraintType: 'UNIQUE', + compModKeyName: 'uniqueKey', + columnKeyProperty: 'unique', + compositeKeyProperty: 'compositeUniqueKey', + constraintNameProperty: 'uniqueKeyConstraintName', + optionsProperty: 'uniqueKeyOptions', +}; + +/** + * Get all modify UK scripts (both composite and regular) + * + * @param {Object} collection + * @param {Object} options + * @return {string[]} + */ +const getModifyUkScripts = (collection, options) => { + return getModifyKeyScripts(collection, UNIQUE_KEY_CONFIG, options); +}; + +module.exports = { + getModifyUkScripts, +}; From ffc40153a4166405019d80a3a0275b22fb0ea3be Mon Sep 17 00:00:00 2001 From: Taras Dubyk Date: Thu, 6 Nov 2025 14:01:51 +0200 Subject: [PATCH 2/5] add commenting deactivated pk, uk constraints --- .../entityHelper/sharedKeyConstraintHelper.js | 96 +++++++++++++++++-- 1 file changed, 90 insertions(+), 6 deletions(-) diff --git a/forward_engineering/helpers/alterScriptHelpers/entityHelper/sharedKeyConstraintHelper.js b/forward_engineering/helpers/alterScriptHelpers/entityHelper/sharedKeyConstraintHelper.js index 9fb8494..212e0b4 100644 --- a/forward_engineering/helpers/alterScriptHelpers/entityHelper/sharedKeyConstraintHelper.js +++ b/forward_engineering/helpers/alterScriptHelpers/entityHelper/sharedKeyConstraintHelper.js @@ -4,8 +4,60 @@ const { getEntityName } = require('../../../utils/general'); const { assignTemplates } = require('../../../utils/assignTemplates'); const templates = require('../../../configs/templates'); const { getTerminator } = require('../../optionsHelper'); +const { commentIfDeactivated } = require('../../commentIfDeactivated'); -const amountOfColumnsInRegularKey = 1; +/** + * Convert keys to string with proper handling of activated/deactivated columns + * @param {Array<{name: string, isActivated: boolean}>} keys + * @param {boolean} isParentActivated - whether the parent table is activated + * @return {string} + */ +const keysToString = (keys, isParentActivated) => { + if (!Array.isArray(keys) || keys.length === 0) { + return ''; + } + + const splitter = ', '; + let deactivatedKeys = []; + const processedKeys = keys + .reduce((keysArray, key) => { + const keyName = `[${key.name}]`; + + if (!_.get(key, 'isActivated', true)) { + deactivatedKeys.push(keyName); + return keysArray; + } + + return [...keysArray, keyName]; + }, []) + .filter(Boolean); + + // If parent is not activated or no activated keys, + // return all keys without commenting, to avoid nested comments + if (!isParentActivated || processedKeys.length === 0) { + return keys.map(key => `[${key.name}]`).join(splitter); + } + + // If no deactivated keys, return activated keys only + if (deactivatedKeys.length === 0) { + return processedKeys.join(splitter); + } + + // Mix of activated and deactivated keys + return ( + processedKeys.join(splitter) + + commentIfDeactivated(splitter + deactivatedKeys.join(splitter), { isActivated: false }, true) + ); +}; + +/** + * Get only activated keys as string (for when we don't support partial deactivation) + * @param {Array<{name: string, isActivated: boolean}>} keys + * @return {string} + */ +const activeKeysToString = keys => { + return keys?.map(key => `[${key.name}]`).join(', '); +}; /** * Configuration object for key constraint handling @@ -72,6 +124,8 @@ const getAddCompositeKeyScripts = (collection, config, options) => { const schemaName = collection.compMod?.keyspaceName; const fullName = getTableName(tableName, schemaName); + const isTableActivated = _.get(collectionSchema, 'isActivated', true); + return newKeys .map(newKey => { const columns = getCompositeKeyColumnNames( @@ -84,18 +138,30 @@ const getAddCompositeKeyScripts = (collection, config, options) => { return null; } - const columnsStr = columns.map(col => `[${col.name}]`).join(', '); + // For PK: use activeKeysToString (PK columns can't be deactivated) + // For UK: use keysToString to handle deactivated columns + const isPrimaryKey = config.constraintType === 'PRIMARY KEY'; + const columnsStr = isPrimaryKey ? activeKeysToString(columns) : keysToString(columns, isTableActivated); + const constraintName = newKey.constraintName ? `[${newKey.constraintName}]` : ''; const statement = constraintName ? `CONSTRAINT ${constraintName} ${config.constraintType} NONCLUSTERED (${columnsStr}) NOT ENFORCED` : `${config.constraintType} NONCLUSTERED (${columnsStr}) NOT ENFORCED`; - return assignTemplates(templates.alterTableAddConstraint, { + const script = assignTemplates(templates.alterTableAddConstraint, { tableName: fullName, constraint: statement, terminator, }); + + // Determine if the constraint should be activated + // For PK: all columns are always activated, so check table activation only + // For UK: check if at least one column is activated AND table is activated + const atLeastOneColumnActivated = isPrimaryKey || columns.some(col => _.get(col, 'isActivated', true)); + const isConstraintActivated = isTableActivated && atLeastOneColumnActivated; + + return commentIfDeactivated(script, { isActivated: isConstraintActivated }); }) .filter(Boolean); }; @@ -129,6 +195,8 @@ const getDropCompositeKeyScripts = (collection, config, options) => { const schemaName = collection.compMod?.keyspaceName; const fullName = getTableName(tableName, schemaName); + const isTableActivated = _.get(collectionSchema, 'isActivated', true); + return oldKeys .map(oldKey => { const constraintName = oldKey.constraintName; @@ -136,11 +204,13 @@ const getDropCompositeKeyScripts = (collection, config, options) => { return null; } - return assignTemplates(templates.alterTable, { + const script = assignTemplates(templates.alterTable, { tableName: fullName, command: `DROP CONSTRAINT [${constraintName}]`, terminator, }); + + return commentIfDeactivated(script, { isActivated: isTableActivated }); }) .filter(Boolean); }; @@ -234,6 +304,8 @@ const getAddRegularKeyScripts = (collection, config, options) => { const schemaName = collection.compMod?.keyspaceName; const fullName = getTableName(tableName, schemaName); + const isTableActivated = _.get(collectionSchema, 'isActivated', true); + return _.toPairs(collection.properties) .filter(([name, jsonSchema]) => { if (wasFieldChangedToBeARegularKey(jsonSchema, collection, config)) { @@ -249,11 +321,19 @@ const getAddRegularKeyScripts = (collection, config, options) => { ? `CONSTRAINT ${constraintName} ${config.constraintType} NONCLUSTERED ([${name}]) NOT ENFORCED` : `${config.constraintType} NONCLUSTERED ([${name}]) NOT ENFORCED`; - return assignTemplates(templates.alterTableAddConstraint, { + const script = assignTemplates(templates.alterTableAddConstraint, { tableName: fullName, constraint: statement, terminator, }); + + // For PK: column is always activated (PK columns can't be deactivated) + // For UK: check column activation + const isPrimaryKey = config.constraintType === 'PRIMARY KEY'; + const isColumnActivated = isPrimaryKey || _.get(jsonSchema, 'isActivated', true); + const isConstraintActivated = isTableActivated && isColumnActivated; + + return commentIfDeactivated(script, { isActivated: isConstraintActivated }); }) .filter(Boolean); }; @@ -272,6 +352,8 @@ const getDropRegularKeyScripts = (collection, config, options) => { const schemaName = collection.compMod?.keyspaceName; const fullName = getTableName(tableName, schemaName); + const isTableActivated = _.get(collectionSchema, 'isActivated', true); + return _.toPairs(collection.properties) .filter(([name, jsonSchema]) => { if (isFieldNoLongerARegularKey(jsonSchema, collection, config)) { @@ -289,11 +371,13 @@ const getDropRegularKeyScripts = (collection, config, options) => { return null; } - return assignTemplates(templates.alterTable, { + const script = assignTemplates(templates.alterTable, { tableName: fullName, command: `DROP CONSTRAINT [${constraintName}]`, terminator, }); + + return commentIfDeactivated(script, { isActivated: isTableActivated }); }) .filter(Boolean); }; From efe4bcba390bdce7669d06afb7e836b9a0bdb93d Mon Sep 17 00:00:00 2001 From: Taras Dubyk Date: Thu, 6 Nov 2025 15:10:00 +0200 Subject: [PATCH 3/5] remove drop inline pk, uk constraint --- .../entityHelper/primaryKeyHelper.js | 2 - .../entityHelper/sharedKeyConstraintHelper.js | 86 +++---------------- .../entityHelper/uniqueKeyHelper.js | 2 - 3 files changed, 12 insertions(+), 78 deletions(-) diff --git a/forward_engineering/helpers/alterScriptHelpers/entityHelper/primaryKeyHelper.js b/forward_engineering/helpers/alterScriptHelpers/entityHelper/primaryKeyHelper.js index f1ab8a3..16ae297 100644 --- a/forward_engineering/helpers/alterScriptHelpers/entityHelper/primaryKeyHelper.js +++ b/forward_engineering/helpers/alterScriptHelpers/entityHelper/primaryKeyHelper.js @@ -8,8 +8,6 @@ const PRIMARY_KEY_CONFIG = { compModKeyName: 'primaryKey', columnKeyProperty: 'primaryKey', compositeKeyProperty: 'compositePrimaryKey', - constraintNameProperty: 'primaryKeyConstraintName', - optionsProperty: 'primaryKeyOptions', }; /** diff --git a/forward_engineering/helpers/alterScriptHelpers/entityHelper/sharedKeyConstraintHelper.js b/forward_engineering/helpers/alterScriptHelpers/entityHelper/sharedKeyConstraintHelper.js index 212e0b4..52635ab 100644 --- a/forward_engineering/helpers/alterScriptHelpers/entityHelper/sharedKeyConstraintHelper.js +++ b/forward_engineering/helpers/alterScriptHelpers/entityHelper/sharedKeyConstraintHelper.js @@ -66,8 +66,6 @@ const activeKeysToString = keys => { * @property {string} compModKeyName - e.g., 'primaryKey' or 'uniqueKey' * @property {string} columnKeyProperty - e.g., 'primaryKey' or 'unique' * @property {string} compositeKeyProperty - e.g., 'compositePrimaryKey' or 'compositeUniqueKey' - * @property {string} constraintNameProperty - e.g., 'primaryKeyConstraintName' or 'uniqueKeyConstraintName' - * @property {string} optionsProperty - e.g., 'primaryKeyOptions' or 'uniqueKeyOptions' */ /** @@ -262,36 +260,9 @@ const isFieldNoLongerARegularKey = (columnJsonSchema, collection, config) => { return wasTheFieldARegularKey && isNotAnyKey; }; -/** - * Check if regular key was modified - * @param {Object} columnJsonSchema - * @param {Object} collection - * @param {KeyConstraintConfig} config - * @return {boolean} - */ -const wasRegularKeyModified = (columnJsonSchema, collection, config) => { - const oldName = columnJsonSchema.compMod.oldField.name; - const oldJsonSchema = collection.role.properties[oldName] || {}; - - const isRegularKey = columnJsonSchema[config.columnKeyProperty] && !columnJsonSchema[config.compositeKeyProperty]; - const wasTheFieldARegularKey = - oldJsonSchema?.[config.columnKeyProperty] && !oldJsonSchema?.[config.compositeKeyProperty]; - - if (!(isRegularKey && wasTheFieldARegularKey)) { - return false; - } - - const oldOptions = _.get(oldJsonSchema, config.optionsProperty, [{}])[0] || {}; - const newOptions = _.get(columnJsonSchema, config.optionsProperty, [{}])[0] || {}; - - const oldConstraintName = oldOptions.constraintName || ''; - const newConstraintName = newOptions.constraintName || ''; - - return !areConstraintsEqual({ constraintName: oldConstraintName }, { constraintName: newConstraintName }); -}; - /** * Get ADD CONSTRAINT scripts for regular (column-level) keys + * Note: Synapse doesn't support named constraints for column-level keys * @param {Object} collection * @param {KeyConstraintConfig} config * @param {Object} options @@ -308,18 +279,11 @@ const getAddRegularKeyScripts = (collection, config, options) => { return _.toPairs(collection.properties) .filter(([name, jsonSchema]) => { - if (wasFieldChangedToBeARegularKey(jsonSchema, collection, config)) { - return true; - } - return wasRegularKeyModified(jsonSchema, collection, config); + return wasFieldChangedToBeARegularKey(jsonSchema, collection, config); }) .map(([name, jsonSchema]) => { - const options = _.get(jsonSchema, config.optionsProperty, [{}])[0] || {}; - const constraintName = options.constraintName ? `[${options.constraintName}]` : ''; - - const statement = constraintName - ? `CONSTRAINT ${constraintName} ${config.constraintType} NONCLUSTERED ([${name}]) NOT ENFORCED` - : `${config.constraintType} NONCLUSTERED ([${name}]) NOT ENFORCED`; + // Synapse doesn't support constraint names for column-level keys + const statement = `${config.constraintType} NONCLUSTERED ([${name}]) NOT ENFORCED`; const script = assignTemplates(templates.alterTableAddConstraint, { tableName: fullName, @@ -340,46 +304,20 @@ const getAddRegularKeyScripts = (collection, config, options) => { /** * Get DROP CONSTRAINT scripts for regular (column-level) keys + * Note: Synapse doesn't support named constraints for column-level keys, + * so we cannot generate DROP scripts for them. They would need to be + * handled by recreating the column or converting to composite constraints. * @param {Object} collection * @param {KeyConstraintConfig} config * @param {Object} options * @return {string[]} */ const getDropRegularKeyScripts = (collection, config, options) => { - const terminator = getTerminator(options); - const collectionSchema = { ...collection, ..._.omit(collection?.role, 'properties') }; - const tableName = getEntityName(collectionSchema); - const schemaName = collection.compMod?.keyspaceName; - const fullName = getTableName(tableName, schemaName); - - const isTableActivated = _.get(collectionSchema, 'isActivated', true); - - return _.toPairs(collection.properties) - .filter(([name, jsonSchema]) => { - if (isFieldNoLongerARegularKey(jsonSchema, collection, config)) { - return true; - } - return wasRegularKeyModified(jsonSchema, collection, config); - }) - .map(([name, jsonSchema]) => { - const oldName = jsonSchema.compMod.oldField.name; - const oldJsonSchema = collection.role.properties[oldName]; - const oldOptions = _.get(oldJsonSchema, config.optionsProperty, [{}])[0] || {}; - const constraintName = oldOptions.constraintName; - - if (!constraintName) { - return null; - } - - const script = assignTemplates(templates.alterTable, { - tableName: fullName, - command: `DROP CONSTRAINT [${constraintName}]`, - terminator, - }); - - return commentIfDeactivated(script, { isActivated: isTableActivated }); - }) - .filter(Boolean); + // Synapse doesn't support dropping unnamed column-level constraints + // Return empty array - these constraints can only be removed by: + // 1. Dropping and recreating the column + // 2. Converting to composite constraint (which can be named and dropped) + return []; }; /** diff --git a/forward_engineering/helpers/alterScriptHelpers/entityHelper/uniqueKeyHelper.js b/forward_engineering/helpers/alterScriptHelpers/entityHelper/uniqueKeyHelper.js index af178b3..a4e4d8d 100644 --- a/forward_engineering/helpers/alterScriptHelpers/entityHelper/uniqueKeyHelper.js +++ b/forward_engineering/helpers/alterScriptHelpers/entityHelper/uniqueKeyHelper.js @@ -8,8 +8,6 @@ const UNIQUE_KEY_CONFIG = { compModKeyName: 'uniqueKey', columnKeyProperty: 'unique', compositeKeyProperty: 'compositeUniqueKey', - constraintNameProperty: 'uniqueKeyConstraintName', - optionsProperty: 'uniqueKeyOptions', }; /** From edac75ce70bea82b6e8ac69cd5c3b86114740bff Mon Sep 17 00:00:00 2001 From: Taras Dubyk Date: Thu, 6 Nov 2025 15:21:16 +0200 Subject: [PATCH 4/5] remove comment --- .../helpers/alterScriptHelpers/alterEntityHelper.js | 1 - 1 file changed, 1 deletion(-) diff --git a/forward_engineering/helpers/alterScriptHelpers/alterEntityHelper.js b/forward_engineering/helpers/alterScriptHelpers/alterEntityHelper.js index 8b9c197..2e5f956 100644 --- a/forward_engineering/helpers/alterScriptHelpers/alterEntityHelper.js +++ b/forward_engineering/helpers/alterScriptHelpers/alterEntityHelper.js @@ -123,7 +123,6 @@ const alterEntityHelper = (app, options) => { }; const getModifyColumnScript = collection => { - // TODO: entrypoint const collectionSchema = { ...collection, ..._.omit(collection?.role, 'properties') }; const tableName = collectionSchema?.code || collectionSchema?.collectionName || collectionSchema?.name; const schemaName = collectionSchema.compMod?.keyspaceName; From 5eea9f3f268198ea633b66a9513e87f9830ce43a Mon Sep 17 00:00:00 2001 From: Taras Dubyk Date: Thu, 6 Nov 2025 15:38:56 +0200 Subject: [PATCH 5/5] refactor: reduce duplication --- .../entityHelper/sharedKeyConstraintHelper.js | 65 +++++++++++-------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/forward_engineering/helpers/alterScriptHelpers/entityHelper/sharedKeyConstraintHelper.js b/forward_engineering/helpers/alterScriptHelpers/entityHelper/sharedKeyConstraintHelper.js index 52635ab..68d54ab 100644 --- a/forward_engineering/helpers/alterScriptHelpers/entityHelper/sharedKeyConstraintHelper.js +++ b/forward_engineering/helpers/alterScriptHelpers/entityHelper/sharedKeyConstraintHelper.js @@ -94,26 +94,24 @@ const areConstraintsEqual = (oldConstraint, newConstraint) => { }; /** - * Get ADD CONSTRAINT scripts for composite keys + * Check if composite keys have changed and return key data if so * @param {Object} collection * @param {KeyConstraintConfig} config - * @param {Object} options - * @return {string[]} + * @return {{hasChanges: boolean, newKeys: Array, oldKeys: Array, tableInfo: Object} | null} */ -const getAddCompositeKeyScripts = (collection, config, options) => { - const terminator = getTerminator(options); +const getCompositeKeyChangeData = (collection, config) => { const keyDto = collection?.role?.compMod?.[config.compModKeyName] || {}; const newKeys = keyDto.new || []; const oldKeys = keyDto.old || []; if (newKeys.length === 0 && oldKeys.length === 0) { - return []; + return null; } if (newKeys.length === oldKeys.length) { const areKeyArraysEqual = _(oldKeys).differenceWith(newKeys, _.isEqual).isEmpty(); if (areKeyArraysEqual) { - return []; + return null; } } @@ -121,9 +119,36 @@ const getAddCompositeKeyScripts = (collection, config, options) => { const tableName = getEntityName(collectionSchema); const schemaName = collection.compMod?.keyspaceName; const fullName = getTableName(tableName, schemaName); - const isTableActivated = _.get(collectionSchema, 'isActivated', true); + return { + hasChanges: true, + newKeys, + oldKeys, + tableInfo: { + fullName, + isTableActivated, + }, + }; +}; + +/** + * Get ADD CONSTRAINT scripts for composite keys + * @param {Object} collection + * @param {KeyConstraintConfig} config + * @param {Object} options + * @return {string[]} + */ +const getAddCompositeKeyScripts = (collection, config, options) => { + const changeData = getCompositeKeyChangeData(collection, config); + if (!changeData) { + return []; + } + + const terminator = getTerminator(options); + const { newKeys, tableInfo } = changeData; + const { fullName, isTableActivated } = tableInfo; + return newKeys .map(newKey => { const columns = getCompositeKeyColumnNames( @@ -172,28 +197,14 @@ const getAddCompositeKeyScripts = (collection, config, options) => { * @return {string[]} */ const getDropCompositeKeyScripts = (collection, config, options) => { - const terminator = getTerminator(options); - const keyDto = collection?.role?.compMod?.[config.compModKeyName] || {}; - const newKeys = keyDto.new || []; - const oldKeys = keyDto.old || []; - - if (newKeys.length === 0 && oldKeys.length === 0) { + const changeData = getCompositeKeyChangeData(collection, config); + if (!changeData) { return []; } - if (newKeys.length === oldKeys.length) { - const areKeyArraysEqual = _(oldKeys).differenceWith(newKeys, _.isEqual).isEmpty(); - if (areKeyArraysEqual) { - return []; - } - } - - const collectionSchema = { ...collection, ..._.omit(collection?.role, 'properties') }; - const tableName = getEntityName(collectionSchema); - const schemaName = collection.compMod?.keyspaceName; - const fullName = getTableName(tableName, schemaName); - - const isTableActivated = _.get(collectionSchema, 'isActivated', true); + const terminator = getTerminator(options); + const { oldKeys, tableInfo } = changeData; + const { fullName, isTableActivated } = tableInfo; return oldKeys .map(oldKey => {