diff --git a/api/fe.js b/api/fe.js index 64f1b6f..f970853 100644 --- a/api/fe.js +++ b/api/fe.js @@ -1,7 +1,9 @@ const { generateContainerScript } = require('../forward_engineering/api/generateContainerScript'); const { isDropInStatements } = require('../forward_engineering/api/isDropInStatements'); +const { generateScript } = require('../forward_engineering/api/generateScript'); module.exports = { + generateScript, generateContainerScript, isDropInStatements, }; diff --git a/forward_engineering/alterScript/alterScriptBuilder.js b/forward_engineering/alterScript/alterScriptBuilder.js index a5e0923..3c75f21 100644 --- a/forward_engineering/alterScript/alterScriptBuilder.js +++ b/forward_engineering/alterScript/alterScriptBuilder.js @@ -44,6 +44,15 @@ const mapCoreDataForContainerLevelScripts = data => { }; }; +const buildEntityLevelAlterScript = (data, app) => { + const alterScriptDtos = getAlterScriptDtos(data, app); + const shouldApplyDropStatements = data.options?.additionalOptions?.some( + option => option.id === 'applyDropStatements' && option.value, + ); + + return joinAlterScriptDtosIntoScript(alterScriptDtos, shouldApplyDropStatements); +}; + const buildContainerLevelAlterScript = (data, app) => { const preparedData = mapCoreDataForContainerLevelScripts(data); const alterScriptDtos = getAlterScriptDtos(preparedData, app); @@ -66,6 +75,7 @@ const doesContainerLevelAlterScriptContainDropStatements = (data, app) => { module.exports = { doesEntityLevelAlterScriptContainDropStatements, + buildEntityLevelAlterScript, buildContainerLevelAlterScript, doesContainerLevelAlterScriptContainDropStatements, }; diff --git a/forward_engineering/alterScript/alterScriptHelpers/alterEntityHelper.js b/forward_engineering/alterScript/alterScriptHelpers/alterEntityHelper.js index 0148f52..f1e631c 100644 --- a/forward_engineering/alterScript/alterScriptHelpers/alterEntityHelper.js +++ b/forward_engineering/alterScript/alterScriptHelpers/alterEntityHelper.js @@ -11,6 +11,7 @@ const { getRenameColumnScriptDtos } = require('./columnHelpers/alterColumnNameHe const { getModifyEntityCommentsScriptDtos } = require('./entityHelpers/commentsHelper'); const { getModifyPkConstraintsScriptDtos } = require('./entityHelpers/primaryKeyHelper'); const { getModifyUkConstraintsScriptDtos } = require('./entityHelpers/uniqueKeyHelper'); +const { getModifyIndexesScriptDtos } = require('./entityHelpers/indexesHelper'); const { getModifiedDefaultColumnValueScriptDtos } = require('./columnHelpers/defaultValueHelper'); const { getEntityName, @@ -109,13 +110,14 @@ const getModifyCollectionScriptDtos = collection => { }; /** - * @param {Object} collection - * @returns {Array} + * @param {Object} ddlProvider + * @returns {(collection: Object) => Array} */ -const getModifyCollectionKeysScriptDtos = collection => { +const getModifyCollectionKeysScriptDtos = ddlProvider => collection => { const modifyPkConstraintDtos = getModifyPkConstraintsScriptDtos(collection); const modifyUkConstraintDtos = getModifyUkConstraintsScriptDtos(collection); - return [...modifyPkConstraintDtos, ...modifyUkConstraintDtos].filter(Boolean); + const modifyIndexesScriptDtos = getModifyIndexesScriptDtos({ ddlProvider, collection }); + return [...modifyPkConstraintDtos, ...modifyUkConstraintDtos, ...modifyIndexesScriptDtos].filter(Boolean); }; /** @@ -197,7 +199,7 @@ const getEntitiesScripts = (app, inlineDeltaRelationships) => { getDeleteCollectionScriptDto: getDeleteCollectionScriptDto(ddlProvider), getModifyCollectionScriptDtos, getModifyColumnScriptDtos: getModifyColumnScriptDtos(ddlProvider), - getModifyCollectionKeysScriptDtos, + getModifyCollectionKeysScriptDtos: getModifyCollectionKeysScriptDtos(ddlProvider), getAddColumnScriptDtos: getAddColumnScriptDtos(ddlProvider), getDeleteColumnScriptDtos: getDeleteColumnScriptDtos(ddlProvider), }; diff --git a/forward_engineering/alterScript/alterScriptHelpers/entityHelpers/indexesHelper.js b/forward_engineering/alterScript/alterScriptHelpers/entityHelpers/indexesHelper.js new file mode 100644 index 0000000..f6b4715 --- /dev/null +++ b/forward_engineering/alterScript/alterScriptHelpers/entityHelpers/indexesHelper.js @@ -0,0 +1,295 @@ +const { isEqual } = require('lodash'); +const { AlterScriptDto } = require('../../types/AlterScriptDto'); +const { + getSchemaNameFromCollection, + getNamePrefixedWithSchemaName, + wrapInQuotes, + getSchemaOfAlterCollection, + getFullCollectionName, + getEntityName, + isObjectInDeltaModelActivated, +} = require('../../../utils/general'); +const { assignTemplates } = require('../../../utils/assignTemplates'); +const templates = require('../../../ddlProvider/templates'); +const { getIndexCommentsScriptDtos, getModifyIndexCommentsScriptDtos } = require('../indexHelpers/commentsHelper'); +const { addNameToIndexKey } = require('../indexHelpers/addNameToIndexKey'); + +const alterIndexProperties = ['indxCompress']; +const columnProperties = ['indxKey', 'indxIncludeKey']; +// Temporary always drop and recreate index if any of these properties changed +const dropAndRecreateIndexProperties = [ + 'indxType', + 'indxTablespace', + 'indxNullKeys', + ...alterIndexProperties, + ...columnProperties, +]; + +/** + * @param {{ + * oldIndex: Object, + * newIndex: Object + * }} param + * @return {boolean} + */ +const shouldDropAndRecreateIndex = ({ oldIndex, newIndex }) => { + return dropAndRecreateIndexProperties.some(property => !isEqual(oldIndex[property], newIndex[property])); +}; + +/** + * @param {{ + * oldIndex: Object, + * newIndex: Object + * }} param + * @return {boolean} + */ +const areOldIndexDtoAndNewIndexDtoDescribingSameDatabaseIndex = ({ oldIndex, newIndex }) => { + return oldIndex.id === newIndex.id || oldIndex.indxName === newIndex.indxName; +}; + +/** + * @param {{ + * schemaName: string, + * oldIndexName: string, + * newIndexName: string, + * isActivated: boolean + * }} param + * @return {AlterScriptDto} + */ +const alterIndexRenameDto = ({ schemaName, oldIndexName, newIndexName, isActivated }) => { + const ddlOldIndexName = getNamePrefixedWithSchemaName({ + name: oldIndexName, + schemaName, + }); + const ddlNewIndexName = wrapInQuotes(newIndexName); + + const script = assignTemplates({ + template: templates.renameIndex, + templateData: { + oldIndexName: ddlOldIndexName, + newIndexName: ddlNewIndexName, + }, + }); + + return AlterScriptDto.getInstance([script], isActivated, false); +}; + +/** + * @param {{ + * index: Object, + * collection: Object, + * ddlProvider: Object + * }} param + * @return {AlterScriptDto | undefined} + */ +const getCreateIndexScriptDto = ({ index, collection, ddlProvider }) => { + const indexWithAddedKeyNames = addNameToIndexKey({ index, collection }); + const collectionSchema = getSchemaOfAlterCollection(collection); + const tableName = getEntityName(collectionSchema); + + const script = ddlProvider.createIndex(tableName, indexWithAddedKeyNames); + return AlterScriptDto.getInstance([script], true, false); +}; + +/** + * @param {{ + * collection: Object, + * ddlProvider: Object + * }} param + * @return {Array} + */ +const getAddedIndexesScriptDtos = ({ collection, ddlProvider }) => { + const newIndexes = collection?.role?.Indxs || []; + const oldIndexes = collection?.role?.compMod?.Indxs?.old || []; + + return newIndexes + .filter(newIndex => { + const correspondingOldIndex = oldIndexes.find(oldIndex => + areOldIndexDtoAndNewIndexDtoDescribingSameDatabaseIndex({ + oldIndex, + newIndex, + }), + ); + return !correspondingOldIndex; + }) + .map(newIndex => { + return getCreateIndexScriptDto({ + index: newIndex, + collection, + ddlProvider, + }); + }) + .filter(Boolean); +}; + +/** + * @param {{ + * index: Object, + * collection: Object, + * ddlProvider: Object + * }} param + * @return {AlterScriptDto | undefined} + */ +const getDeleteIndexScriptDto = ({ index, collection, ddlProvider }) => { + const schemaName = getSchemaNameFromCollection({ collection }); + const fullIndexName = getNamePrefixedWithSchemaName({ + name: index.indxName, + schemaName, + }); + const script = ddlProvider.dropIndex(fullIndexName); + return AlterScriptDto.getInstance([script], index.isActivated && isObjectInDeltaModelActivated(collection), true); +}; + +/** + * @param {{ + * collection: Object, + * ddlProvider: Object + * }} param + * @return {Array} + */ +const getDeletedIndexesScriptDtos = ({ collection, ddlProvider }) => { + const newIndexes = collection?.role?.compMod?.Indxs?.new || []; + const oldIndexes = collection?.role?.compMod?.Indxs?.old || []; + + return oldIndexes + .filter(oldIndex => { + const correspondingNewIndex = newIndexes.find(newIndex => + areOldIndexDtoAndNewIndexDtoDescribingSameDatabaseIndex({ + oldIndex, + newIndex, + }), + ); + return !correspondingNewIndex; + }) + .map(oldIndex => { + return getDeleteIndexScriptDto({ index: oldIndex, collection, ddlProvider }); + }) + .filter(Boolean); +}; + +/** + * @param {{ + * newIndex: Object, + * oldIndex: Object, + * collection: Object, + * ddlProvider: Object + * }} param + * @return {Array} + */ +const getModifyIndexScriptDto = ({ newIndex, oldIndex, collection, ddlProvider }) => { + const scripts = []; + + const shouldDropAndRecreate = shouldDropAndRecreateIndex({ newIndex, oldIndex }); + if (shouldDropAndRecreate) { + const deleteIndexScriptDto = getDeleteIndexScriptDto({ + index: oldIndex, + collection, + ddlProvider, + }); + const createIndexScriptDto = getCreateIndexScriptDto({ + index: newIndex, + collection, + ddlProvider, + }); + // if an index was recreated, and comment was removed, + // no need to generate separate script to drop comment + scripts.push(deleteIndexScriptDto, createIndexScriptDto); + + if (newIndex.indxDescription) { + const commentDtos = getIndexCommentsScriptDtos({ + index: newIndex, + collection, + }); + scripts.push(...commentDtos); + } + + return scripts; + } + + if (oldIndex.indxName !== newIndex.indxName) { + const schemaName = getSchemaNameFromCollection({ collection }); + const renameScript = alterIndexRenameDto({ + schemaName, + oldIndexName: oldIndex.indxName, + newIndexName: newIndex.indxName, + isActivated: isObjectInDeltaModelActivated(collection) && newIndex.isActivated, + }); + + scripts.push(renameScript); + } + + const commentDtos = getModifyIndexCommentsScriptDtos({ newIndex, oldIndex, collection }); + + if (commentDtos) { + scripts.push(commentDtos); + } + + return scripts; +}; + +/** + * @param {{ + * collection: Object, + * ddlProvider: Object + * }} param + * @return {Array} + */ +const getModifiedIndexesScriptDtos = ({ collection, ddlProvider }) => { + const newIndexes = collection?.role?.compMod?.Indxs?.new || []; + const oldIndexes = collection?.role?.compMod?.Indxs?.old || []; + + return newIndexes + .map(newIndex => { + const correspondingOldIndex = oldIndexes.find(oldIndex => + areOldIndexDtoAndNewIndexDtoDescribingSameDatabaseIndex({ + oldIndex, + newIndex, + }), + ); + if (correspondingOldIndex) { + return { + newIndex, + oldIndex: correspondingOldIndex, + }; + } + return undefined; + }) + .filter(Boolean) + .flatMap(({ newIndex, oldIndex }) => { + return getModifyIndexScriptDto({ + newIndex, + oldIndex, + collection, + ddlProvider, + }); + }) + .filter(Boolean); +}; + +/** + * @param {{ + * ddlProvider: Object, + * collection: Object + * }} param + * @return {Array} + */ +const getModifyIndexesScriptDtos = ({ ddlProvider, collection }) => { + const deletedIndexesScriptDtos = getDeletedIndexesScriptDtos({ + collection, + ddlProvider, + }); + const addedIndexesScriptDtos = getAddedIndexesScriptDtos({ + collection, + ddlProvider, + }); + const modifiedIndexesScriptDtos = getModifiedIndexesScriptDtos({ + collection, + ddlProvider, + }); + + return [...deletedIndexesScriptDtos, ...addedIndexesScriptDtos, ...modifiedIndexesScriptDtos].filter(Boolean); +}; + +module.exports = { + getModifyIndexesScriptDtos, +}; diff --git a/forward_engineering/alterScript/alterScriptHelpers/indexHelpers/addNameToIndexKey.js b/forward_engineering/alterScript/alterScriptHelpers/indexHelpers/addNameToIndexKey.js new file mode 100644 index 0000000..1140ce4 --- /dev/null +++ b/forward_engineering/alterScript/alterScriptHelpers/indexHelpers/addNameToIndexKey.js @@ -0,0 +1,57 @@ +const { toPairs } = require('lodash'); +const { getSchemaNameFromCollection } = require('../../../utils/general'); + +/** + * @param {string} columnId + * @param {Object} collection + * @return {string | undefined} + * */ +const getColumnNameById = ({ columnId, collection }) => { + const collectionProperties = toPairs(collection?.role?.properties || collection?.properties || {}).map( + ([name, value]) => ({ ...value, name }), + ); + const oldProperties = (collection?.role?.compMod?.oldProperties || []).map(property => ({ + ...property, + GUID: property.id, + })); + const properties = collectionProperties.length > 0 ? collectionProperties : oldProperties; + const propertySchema = properties.find(fieldJsonSchema => fieldJsonSchema.GUID === columnId); + + if (propertySchema) { + return propertySchema.name; + } + + return undefined; +}; + +/** + * @param {AlterIndexDto} index + * @param {Object} collection + * @return {Object} + * */ +const addNameToIndexKey = ({ index, collection }) => { + if (!index?.indxKey?.length) { + return index; + } + + const schemaName = getSchemaNameFromCollection({ collection }); + + const columnsWithNames = index.indxKey + .map(column => { + return { + ...column, + name: getColumnNameById({ columnId: column.keyId, collection }), + }; + }) + .filter(column => Boolean(column.name)); + + return { + ...index, + schemaName, + indxKey: columnsWithNames, + }; +}; + +module.exports = { + addNameToIndexKey, +}; diff --git a/forward_engineering/alterScript/alterScriptHelpers/indexHelpers/commentsHelper.js b/forward_engineering/alterScript/alterScriptHelpers/indexHelpers/commentsHelper.js new file mode 100644 index 0000000..4fa0526 --- /dev/null +++ b/forward_engineering/alterScript/alterScriptHelpers/indexHelpers/commentsHelper.js @@ -0,0 +1,52 @@ +const { AlterScriptDto } = require('../../types/AlterScriptDto'); +const { + getSchemaNameFromCollection, + getNamePrefixedWithSchemaName, + wrapInQuotes, + isObjectInDeltaModelActivated, +} = require('../../../utils/general'); +const { + getIndexCommentStatement, + dropIndexCommentStatement, +} = require('../../../ddlProvider/ddlHelpers/comment/commentHelper'); + +/** + * @param {{ + * newIndex: Object, + * oldIndex: Object, + * collection: Object + * }} param + * @returns {AlterScriptDto|undefined} + */ +const getModifyIndexCommentsScriptDtos = ({ newIndex, oldIndex, collection }) => { + const newDescription = newIndex.indxDescription; + const oldDescription = oldIndex.indxDescription; + const schemaName = getSchemaNameFromCollection({ collection }); + const fullIndexName = getNamePrefixedWithSchemaName({ + name: newIndex.indxName, + schemaName, + }); + + const isActivated = isObjectInDeltaModelActivated(collection) && newIndex.isActivated; + + if (newDescription && newDescription !== oldDescription) { + const script = getIndexCommentStatement({ + indexName: fullIndexName, + description: newDescription, + }); + return AlterScriptDto.getInstance([script], isActivated, false); + } + + if (oldDescription && !newDescription) { + const script = dropIndexCommentStatement({ + indexName: fullIndexName, + }); + return AlterScriptDto.getInstance([script], isActivated, true); + } + + return undefined; +}; + +module.exports = { + getModifyIndexCommentsScriptDtos, +}; diff --git a/forward_engineering/api.js b/forward_engineering/api.js index 20f1620..29cd49e 100644 --- a/forward_engineering/api.js +++ b/forward_engineering/api.js @@ -2,11 +2,10 @@ const { generateContainerScript } = require('./api/generateContainerScript'); const { isDropInStatements } = require('./api/isDropInStatements'); const { testConnection } = require('../shared/api/testConnection'); const { applyToInstance } = require('./api/applyToInstance'); +const { generateScript } = require('./api/generateScript'); module.exports = { - generateScript(data, logger, callback, app) { - throw new Error('Not implemented'); - }, + generateScript, generateViewScript(data, logger, callback, app) { throw new Error('Not implemented'); diff --git a/forward_engineering/api/generateScript.js b/forward_engineering/api/generateScript.js new file mode 100644 index 0000000..d2987d8 --- /dev/null +++ b/forward_engineering/api/generateScript.js @@ -0,0 +1,16 @@ +const { buildEntityLevelAlterScript } = require('../alterScript/alterScriptBuilder'); + +function generateScript(data, logger, callback, app) { + try { + const script = buildEntityLevelAlterScript(data, app); + callback(null, script); + } catch (error) { + logger.log('error', { message: error.message, stack: error.stack }, 'Oracle Forward-Engineering Error'); + + callback({ message: error.message, stack: error.stack }); + } +} + +module.exports = { + generateScript, +}; diff --git a/forward_engineering/ddlProvider/ddlHelpers/comment/commentHelper.js b/forward_engineering/ddlProvider/ddlHelpers/comment/commentHelper.js index e5d85a6..0bb9aff 100644 --- a/forward_engineering/ddlProvider/ddlHelpers/comment/commentHelper.js +++ b/forward_engineering/ddlProvider/ddlHelpers/comment/commentHelper.js @@ -155,6 +155,18 @@ const dropTableColumnCommentStatement = ({ tableName, columnName }) => { }); }; +/** + * @param {{ indexName: string }} + * @returns {string} + */ +const dropIndexCommentStatement = ({ indexName }) => { + return getCommentStatement({ + objectName: indexName, + objectType: OBJECT_TYPE.index, + mode: COMMENT_MODE.remove, + }); +}; + module.exports = { getColumnCommentStatement, getSchemaCommentStatement, @@ -165,4 +177,5 @@ module.exports = { dropSchemaCommentStatement, dropTableCommentStatement, dropTableColumnCommentStatement, + dropIndexCommentStatement, }; diff --git a/forward_engineering/ddlProvider/ddlHelpers/view/getViewData.js b/forward_engineering/ddlProvider/ddlHelpers/view/getViewData.js index 9b9d2d5..ba3bc3c 100644 --- a/forward_engineering/ddlProvider/ddlHelpers/view/getViewData.js +++ b/forward_engineering/ddlProvider/ddlHelpers/view/getViewData.js @@ -1,4 +1,4 @@ -const { wrapInQuotes } = require('../../../utils/general'); +const { wrapInQuotes, getNamePrefixedWithSchemaName } = require('../../../utils/general'); /** * @param {{ key?: object }} @@ -37,7 +37,10 @@ const getViewData = ({ keys }) => { return result; } - const tableName = `${wrapInQuotes(key.dbName)}.${wrapInQuotes(key.tableName)}`; + const tableName = getNamePrefixedWithSchemaName({ + name: key.tableName, + schemaName: key.dbName, + }); if (!result.tables.includes(tableName)) { result.tables.push(tableName); diff --git a/forward_engineering/ddlProvider/ddlProvider.js b/forward_engineering/ddlProvider/ddlProvider.js index c9a824b..5dca1a6 100644 --- a/forward_engineering/ddlProvider/ddlProvider.js +++ b/forward_engineering/ddlProvider/ddlProvider.js @@ -1,4 +1,4 @@ -const { toUpper, isEmpty, trim } = require('lodash'); +const { toUpper, isEmpty, trim, get } = require('lodash'); const templates = require('./templates'); const defaultTypes = require('../configs/defaultTypes.js'); const descriptors = require('../configs/descriptors.js'); @@ -424,7 +424,8 @@ module.exports = (baseProvider, options, app) => { }, hydrateIndex(indexData, tableData, schemaData) { - return { ...indexData, schemaName: schemaData.schemaName }; + const isParentActivated = get(tableData, '[0].isActivated', true); + return { ...indexData, schemaName: schemaData.schemaName, isParentActivated }; }, createIndex(tableName, index) { @@ -442,11 +443,26 @@ module.exports = (baseProvider, options, app) => { templateData: { indexType, indexName, indexOptions, indexTableName }, }); const commentStatement = getIndexCommentStatement({ indexName, description: index.indxDescription }); - const createIndexStatement = statement + commentStatement; - return commentIfDeactivated(createIndexStatement, { - isActivated: index.isActivated, + let finalStatement = commentIfDeactivated(statement, { + isActivated: index.isActivated && index.isParentActivated, }); + + if (commentStatement) { + finalStatement += + '\n' + + commentIfDeactivated(commentStatement, { + isPartOfLine: true, + isActivated: index.isActivated && index.isParentActivated, + }) + + '\n'; + } + + return finalStatement; + }, + + dropIndex(name) { + return assignTemplates({ template: templates.dropIndex, templateData: { name } }); }, hydrateViewColumn(data) { diff --git a/forward_engineering/ddlProvider/templates.js b/forward_engineering/ddlProvider/templates.js index 57906fc..3a07a4f 100644 --- a/forward_engineering/ddlProvider/templates.js +++ b/forward_engineering/ddlProvider/templates.js @@ -66,4 +66,8 @@ module.exports = { dropView: 'DROP VIEW ${viewName};', renameTable: 'RENAME TABLE ${oldTableName} TO ${newTableName};', + + renameIndex: 'RENAME INDEX ${oldIndexName} TO ${newIndexName};', + + dropIndex: 'DROP INDEX ${name};', };