From b22db396dae6d6f32a42473c90af85caf9df304b Mon Sep 17 00:00:00 2001 From: Alik Rakhmonov Date: Wed, 15 Oct 2025 17:11:11 +0200 Subject: [PATCH 1/2] HCK-13050: generate FK in delta models inline or separate --- .../alterScript/alterScriptFromDeltaHelper.js | 114 ++++++++++++++++-- .../alterScriptHelpers/alterEntityHelper.js | 25 +++- .../alterRelationshipsHelper.js | 1 + .../ddlProvider/ddlProvider.js | 2 +- 4 files changed, 130 insertions(+), 12 deletions(-) diff --git a/forward_engineering/alterScript/alterScriptFromDeltaHelper.js b/forward_engineering/alterScript/alterScriptFromDeltaHelper.js index 0b1f348..20a3c5e 100644 --- a/forward_engineering/alterScript/alterScriptFromDeltaHelper.js +++ b/forward_engineering/alterScript/alterScriptFromDeltaHelper.js @@ -78,6 +78,57 @@ const getAlterContainersScriptDtos = ({ collection }) => { return [...addContainersScriptDtos, ...deleteContainersScriptDtos, ...modifyContainersScriptDtos].filter(Boolean); }; +const sortCollectionsByRelationships = (collections, relationships) => { + const collectionToChildren = new Map(); // Map of collection IDs to their children + const collectionParentCount = new Map(); // Track how many parents each collection has + + // Initialize maps + for (const collection of collections) { + collectionToChildren.set(collection.role.id, []); + collectionParentCount.set(collection.role.id, 0); + } + + for (const relationship of relationships) { + const parent = relationship.role.parentCollection; + const child = relationship.role.childCollection; + if (collectionToChildren.has(parent)) { + collectionToChildren.get(parent).push(child); + } + collectionParentCount.set(child, (collectionParentCount.get(child) || 0) + 1); + } + + // Find collections with no parents + const queue = collections + .filter(collection => collectionParentCount.get(collection.role.id) === 0) + .map(collection => collection.role.id); + + const sortedIds = []; + + // Sort collections + while (queue.length > 0) { + const current = queue.shift(); + sortedIds.push(current); + + for (const child of collectionToChildren.get(current) || []) { + collectionParentCount.set(child, collectionParentCount.get(child) - 1); + if (collectionParentCount.get(child) <= 0) { + queue.push(child); + } + } + } + + // Add any unvisited collection + for (const collection of collections) { + if (!sortedIds.includes(collection.role.id)) { + sortedIds.unshift(collection.role.id); + } + } + + // Map back to collection objects in sorted order + const idToCollection = Object.fromEntries(collections.map(c => [c.role.id, c])); + return sortedIds.map(id => idToCollection[id]); +}; + /** * @param dto {{ * collection: Object, @@ -96,6 +147,7 @@ const getAlterCollectionsScriptDtos = ({ modelDefinitions, internalDefinitions, externalDefinitions, + inlineDeltaRelationships, }) => { const createScriptsData = [] .concat(collection.properties?.entities?.properties?.added?.items) @@ -112,9 +164,19 @@ const getAlterCollectionsScriptDtos = ({ .filter(Boolean) .map(item => Object.values(item.properties)[0]); - const createCollectionsScriptDtos = createScriptsData - .filter(collection => collection.compMod?.created) - .map(getAddCollectionScriptDto({ app, dbVersion, modelDefinitions, internalDefinitions, externalDefinitions })); + const createCollectionsScriptDtos = sortCollectionsByRelationships( + createScriptsData.filter(collection => collection.compMod?.created), + inlineDeltaRelationships, + ).map( + getAddCollectionScriptDto({ + app, + dbVersion, + modelDefinitions, + internalDefinitions, + externalDefinitions, + inlineDeltaRelationships, + }), + ); const deleteCollectionScriptDtos = deleteScriptsData .filter(collection => collection.compMod?.deleted) @@ -138,13 +200,13 @@ const getAlterCollectionsScriptDtos = ({ ); return [ - ...createCollectionsScriptDtos, ...deleteCollectionScriptDtos, ...modifyCollectionScriptDtos, ...addColumnScriptDtos, ...deleteColumnScriptDtos, ...modifyColumnScriptDtos, ...modifyCollectionKeysScriptDtos, + ...createCollectionsScriptDtos, ].filter(Boolean); }; @@ -261,26 +323,35 @@ const getAlterModelDefinitionsScriptDtos = ({ /** * @return Array * */ -const getAlterRelationshipsScriptDtos = ({ collection, app }) => { +const getAlterRelationshipsScriptDtos = ({ collection, app, ignoreRelationshipIDs = [] }) => { const ddlProvider = require('../ddlProvider/ddlProvider')(null, null, app); const addedRelationships = [] .concat(collection.properties?.relationships?.properties?.added?.items) .filter(Boolean) .map(item => Object.values(item.properties)[0]) - .filter(relationship => relationship?.role?.compMod?.created); + .filter( + relationship => + relationship?.role?.compMod?.created && !ignoreRelationshipIDs.includes(relationship?.role?.id), + ); const deletedRelationships = [] .concat(collection.properties?.relationships?.properties?.deleted?.items) .filter(Boolean) .map(item => Object.values(item.properties)[0]) - .filter(relationship => relationship?.role?.compMod?.deleted); + .filter( + relationship => + relationship?.role?.compMod?.deleted && !ignoreRelationshipIDs.includes(relationship?.role?.id), + ); const modifiedRelationships = [] .concat(collection.properties?.relationships?.properties?.modified?.items) .filter(Boolean) .map(item => Object.values(item.properties)[0]) - .filter(relationship => relationship?.role?.compMod?.modified); + .filter( + relationship => + relationship?.role?.compMod?.modified && !ignoreRelationshipIDs.includes(relationship?.role?.id), + ); const deleteFkScriptDtos = getDeleteForeignKeyScriptDtos(ddlProvider)(deletedRelationships); const addFkScriptDtos = getAddForeignKeyScriptDtos(ddlProvider)(addedRelationships); @@ -350,6 +421,25 @@ const getAlterContainersSequencesScriptDtos = ({ collection, app }) => { ); }; +const getInlineRelationships = ({ collection, options }) => { + if (options?.scriptGenerationOptions?.feActiveOptions?.foreignKeys !== 'inline') { + return []; + } + + const addedCollectionIDs = [] + .concat(collection.properties?.entities?.properties?.added?.items) + .filter(item => Object.values(item.properties)?.[0]?.compMod?.created) + .map(item => Object.values(item.properties)[0].role.id); + + const addedRelationships = [] + .concat(collection.properties?.relationships?.properties?.added?.items) + .filter(Boolean) + .map(item => Object.values(item.properties)[0]) + .filter(r => r?.role?.compMod?.created && addedCollectionIDs.includes(r?.role?.childCollection)); + + return addedRelationships; +}; + /** * @param data {CoreData} * @param app {App} @@ -367,6 +457,7 @@ const getAlterScriptDtos = (data, app) => { const internalDefinitions = JSON.parse(data.internalDefinitions); const externalDefinitions = JSON.parse(data.externalDefinitions); const dbVersion = data.modelData[0]?.dbVersion; + const inlineDeltaRelationships = getInlineRelationships({ collection, options: data.options }); const containersScriptDtos = getAlterContainersScriptDtos({ collection }); const collectionsScriptDtos = getAlterCollectionsScriptDtos({ collection, @@ -375,6 +466,7 @@ const getAlterScriptDtos = (data, app) => { modelDefinitions, internalDefinitions, externalDefinitions, + inlineDeltaRelationships, }); const viewScriptDtos = getAlterViewScriptDtos(collection, app); const modelDefinitionsScriptDtos = getAlterModelDefinitionsScriptDtos({ @@ -385,7 +477,11 @@ const getAlterScriptDtos = (data, app) => { internalDefinitions, externalDefinitions, }); - const relationshipScriptDtos = getAlterRelationshipsScriptDtos({ collection, app }); + const relationshipScriptDtos = getAlterRelationshipsScriptDtos({ + collection, + app, + ignoreRelationshipIDs: inlineDeltaRelationships.map(relationship => relationship.role.id), + }); const containersSequencesScriptDtos = getAlterContainersSequencesScriptDtos({ collection, app }); return [ diff --git a/forward_engineering/alterScript/alterScriptHelpers/alterEntityHelper.js b/forward_engineering/alterScript/alterScriptHelpers/alterEntityHelper.js index 5b3ba17..05252f8 100644 --- a/forward_engineering/alterScript/alterScriptHelpers/alterEntityHelper.js +++ b/forward_engineering/alterScript/alterScriptHelpers/alterEntityHelper.js @@ -24,12 +24,13 @@ const { isParentContainerActivated, isObjectInDeltaModelActivated, } = require('../../utils/general'); +const { getRelationshipName } = require('./alterRelationshipsHelper'); /** * @return {(collection: AlterCollectionDto) => AlterScriptDto | undefined} * */ const getAddCollectionScriptDto = - ({ app, dbVersion, modelDefinitions, internalDefinitions, externalDefinitions }) => + ({ app, dbVersion, modelDefinitions, internalDefinitions, externalDefinitions, inlineDeltaRelationships = [] }) => collection => { const ddlProvider = require('../../ddlProvider/ddlProvider')(null, null, app); const { createColumnDefinitionBySchema } = require('./createColumnDefinition')(app); @@ -58,11 +59,31 @@ const getAddCollectionScriptDto = const checkConstraints = (jsonSchema.chkConstr || []).map(check => ddlProvider.createCheckConstraint(ddlProvider.hydrateCheckConstraint(check)), ); + const foreignKeyConstraints = inlineDeltaRelationships + .filter(relationship => relationship.role.childCollection === collection.role.id) + .map(relationship => { + const compMod = relationship.role.compMod; + const relationshipName = + compMod.code?.new || compMod.name?.new || getRelationshipName(relationship) || ''; + return ddlProvider.createForeignKeyConstraint({ + name: relationshipName, + foreignKey: compMod.child.collection.fkFields, + primaryKey: compMod.parent.collection.fkFields, + customProperties: compMod.customProperties?.new, + foreignTable: compMod.child.collection.name, + foreignSchemaName: compMod.child.bucket.name, + foreignTableActivated: compMod.child.collection.isActivated, + primaryTable: compMod.parent.collection.name, + primarySchemaName: compMod.parent.bucket.name, + primaryTableActivated: compMod.parent.collection.isActivated, + isActivated: Boolean(relationship.role?.compMod?.isActivated?.new), + }); + }); const tableData = { name: getEntityName(jsonSchema), columns: columnDefinitions.map(ddlProvider.convertColumnDefinition), checkConstraints: checkConstraints, - foreignKeyConstraints: [], + foreignKeyConstraints, schemaData, columnDefinitions, dbData: { dbVersion }, diff --git a/forward_engineering/alterScript/alterScriptHelpers/alterRelationshipsHelper.js b/forward_engineering/alterScript/alterScriptHelpers/alterRelationshipsHelper.js index 2b0dbf4..7d1f20c 100644 --- a/forward_engineering/alterScript/alterScriptHelpers/alterRelationshipsHelper.js +++ b/forward_engineering/alterScript/alterScriptHelpers/alterRelationshipsHelper.js @@ -160,4 +160,5 @@ module.exports = { getDeleteForeignKeyScriptDtos, getModifyForeignKeyScriptDtos, getAddForeignKeyScriptDtos, + getRelationshipName, }; diff --git a/forward_engineering/ddlProvider/ddlProvider.js b/forward_engineering/ddlProvider/ddlProvider.js index f99cd77..52905d7 100644 --- a/forward_engineering/ddlProvider/ddlProvider.js +++ b/forward_engineering/ddlProvider/ddlProvider.js @@ -357,7 +357,7 @@ module.exports = (baseProvider, options, app) => { additionalPropertiesForForeignKey(customProperties); const foreignKeyStatement = assignTemplates(templates.createForeignKeyConstraint, { - primaryTable: getNamePrefixedWithSchemaName(primaryTable, primarySchemaName || schemaData.schemaName), + primaryTable: getNamePrefixedWithSchemaName(primaryTable, primarySchemaName || schemaData?.schemaName), name: name ? `CONSTRAINT ${wrapInQuotes(name)}` : '', foreignKey: areKeysActivated ? foreignKeysToString(foreignKey) : foreignActiveKeysToString(foreignKey), primaryKey: areKeysActivated ? foreignKeysToString(primaryKey) : foreignActiveKeysToString(primaryKey), From 0293387c68cc5656cfb4db06eeffb13ea75209a5 Mon Sep 17 00:00:00 2001 From: Alik Rakhmonov Date: Thu, 16 Oct 2025 11:56:49 +0200 Subject: [PATCH 2/2] fix --- .../alterScript/alterScriptFromDeltaHelper.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/forward_engineering/alterScript/alterScriptFromDeltaHelper.js b/forward_engineering/alterScript/alterScriptFromDeltaHelper.js index 20a3c5e..9675682 100644 --- a/forward_engineering/alterScript/alterScriptFromDeltaHelper.js +++ b/forward_engineering/alterScript/alterScriptFromDeltaHelper.js @@ -428,13 +428,12 @@ const getInlineRelationships = ({ collection, options }) => { const addedCollectionIDs = [] .concat(collection.properties?.entities?.properties?.added?.items) - .filter(item => Object.values(item.properties)?.[0]?.compMod?.created) + .filter(item => item && Object.values(item.properties)?.[0]?.compMod?.created) .map(item => Object.values(item.properties)[0].role.id); const addedRelationships = [] .concat(collection.properties?.relationships?.properties?.added?.items) - .filter(Boolean) - .map(item => Object.values(item.properties)[0]) + .map(item => item && Object.values(item.properties)[0]) .filter(r => r?.role?.compMod?.created && addedCollectionIDs.includes(r?.role?.childCollection)); return addedRelationships;