diff --git a/forward_engineering/configs/templates.js b/forward_engineering/configs/templates.js index 18c21f71..e88fb4d7 100644 --- a/forward_engineering/configs/templates.js +++ b/forward_engineering/configs/templates.js @@ -6,7 +6,7 @@ module.exports = { dropSchema: 'DROP SCHEMA IF EXISTS ${name};\n', createTable: - 'CREATE${orReplace}${temporary}${transient} TABLE${tableIfNotExists}\n' + + 'CREATE${orReplace}${temporary}${transient}${hybrid} TABLE${tableIfNotExists}\n' + '\t${name} (\n' + '\t\t${column_definitions}' + '${out_of_line_constraints}\n' + @@ -52,6 +52,12 @@ module.exports = { '\t\t${column_definitions}${out_of_line_constraints}\n' + '\t)${tableOptions};\n', + createIndex: + 'CREATE${orReplace} INDEX${ifNotExists} ${name}\n' + + '\tON ${tableName} (\n' + + '\t\t${keys}\n' + + '\t)${includeKeys};', + columnDefinition: '${name} ${type}${collation}${default}${identity}${autoincrement}${not_nul}${inline_constraint}${comment}${tag}', diff --git a/forward_engineering/ddlProvider.js b/forward_engineering/ddlProvider.js index 006878d3..5ddff6ec 100644 --- a/forward_engineering/ddlProvider.js +++ b/forward_engineering/ddlProvider.js @@ -58,12 +58,14 @@ const { } = require('./helpers/tagHelper'); const { createView, hydrateView, hydrateViewColumn } = require('./helpers/viewHelper'); const { hydrateJsonSchemaColumn } = require('./helpers/hydrateJsonSchema'); +const { prepareIndexKeys } = require('./helpers/indexHelper'); const DEFAULT_SNOWFLAKE_SEQUENCE_START = 1; const DEFAULT_SNOWFLAKE_SEQUENCE_INCREMENT = 1; module.exports = (baseProvider, options, app) => { - const { tab, hasType, clean } = app.require('@hackolade/ddl-fe-utils').general; + const { tab, hasType, clean, divideIntoActivatedAndDeactivated, checkAllKeysDeactivated } = + app.require('@hackolade/ddl-fe-utils').general; const scriptFormat = options?.targetScriptOptions?.keyword || FORMATS.SNOWSIGHT; const keyHelper = require('./helpers/keyHelper')(app); @@ -314,6 +316,7 @@ module.exports = (baseProvider, options, app) => { createTable(tableData, isActivated) { const schemaName = get(tableData, 'schemaData.schemaName'); + const hybrid = preSpace(tableData.hybrid && 'HYBRID'); const temporary = preSpace(tableData.temporary && 'TEMPORARY'); const transient = preSpace(tableData.transient && !tableData.temporary && 'TRANSIENT'); const orReplace = preSpace(tableData.orReplace && 'OR REPLACE'); @@ -321,6 +324,7 @@ module.exports = (baseProvider, options, app) => { const clusterKeys = preSpace( !isEmpty(tableData.clusteringKey) && + !hybrid && 'CLUSTER BY (' + (isActivated ? foreignKeysToString(tableData.isCaseSensitive, tableData.clusteringKey) @@ -469,6 +473,7 @@ module.exports = (baseProvider, options, app) => { return assignTemplates(templates.createTable, { name: tableData.fullName, + hybrid, temporary, transient, tableIfNotExists, @@ -579,6 +584,73 @@ module.exports = (baseProvider, options, app) => { }; }, + hydrateIndex(indexData, tableData, schemaData) { + const firstTab = head(tableData) ?? {}; + + if (!firstTab.hybrid) { + return; + } + + const schemaName = getName(firstTab.isCaseSensitive, get(schemaData, 'schemaName')); + const databaseName = getName(firstTab.isCaseSensitive, get(schemaData, 'databaseName')); + const tableName = getName(firstTab.isCaseSensitive, firstTab.code || firstTab.collectionName); + const fullTableName = getFullName(databaseName, getFullName(schemaName, tableName)); + + return { + indxName: getName(firstTab.isCaseSensitive, indexData.indxName), + isTableCaseSensitive: firstTab.isCaseSensitive, + fullTableName, + indxKey: indexData?.indxKey?.map(key => ({ + name: key.name, + isActivated: key.isActivated, + })), + indxIncludeKey: indexData?.indxIncludeKey?.map(key => ({ + name: key.name, + isActivated: key.isActivated, + })), + isActivated: indexData?.isActivated, + ifNotExists: indexData?.ifNotExist, + orReplace: indexData?.orReplace, + }; + }, + + createIndex(_tableName, index, dbData, isParentActivated = true) { + if (isEmpty(index.indxKey) || !index.indxName) { + return ''; + } + + const allDeactivated = checkAllKeysDeactivated(index.indxKey || []); + const wholeStatementCommented = index.isActivated === false || !isParentActivated || allDeactivated; + + const includeKeys = prepareIndexKeys({ + indexKeys: index.indxIncludeKey, + wholeStatementCommented, + isCaseSensitive: index.isTableCaseSensitive, + divideIntoActivatedAndDeactivated, + }); + const includeKeysStatement = !includeKeys ? '' : preSpace(`INCLUDE ( ${includeKeys} )`); + + const indexStatement = assignTemplates(templates.createIndex, { + name: index.indxName, + tableName: index.fullTableName, + orReplace: preSpace(index.orReplace && 'OR REPLACE'), + ifNotExists: preSpace(index.ifNotExists && 'IF NOT EXISTS'), + keys: prepareIndexKeys({ + indexKeys: index.indxKey, + wholeStatementCommented, + isCaseSensitive: index.isTableCaseSensitive, + divideIntoActivatedAndDeactivated, + }), + includeKeys: includeKeysStatement, + }); + + if (wholeStatementCommented) { + return commentIfDeactivated(indexStatement, { isActivated: false }); + } else { + return indexStatement; + } + }, + createView(viewData, dbData, isActivated) { return createView({ viewData, @@ -840,6 +912,7 @@ module.exports = (baseProvider, options, app) => { ...tableData, fullName, name: tableName, + hybrid: firstTab.hybrid, temporary: firstTab.temporary, transient: firstTab.transient, external: firstTab.external, diff --git a/forward_engineering/helpers/commentHelpers/commentDeactivatedHelper.js b/forward_engineering/helpers/commentHelpers/commentDeactivatedHelper.js index 6b2a1d61..c4c1c632 100644 --- a/forward_engineering/helpers/commentHelpers/commentDeactivatedHelper.js +++ b/forward_engineering/helpers/commentHelpers/commentDeactivatedHelper.js @@ -3,7 +3,7 @@ const STARTS_QUERY = ['//']; const commentIfDeactivated = (statement, data, isPartOfLine) => { if (data.isActivated === false) { if (isPartOfLine) { - return '// ' + statement; + return '/* ' + statement + ' */'; } else if (statement.includes('\n')) { return statement .split('\n') diff --git a/forward_engineering/helpers/indexHelper.js b/forward_engineering/helpers/indexHelper.js new file mode 100644 index 00000000..435372d4 --- /dev/null +++ b/forward_engineering/helpers/indexHelper.js @@ -0,0 +1,33 @@ +const { commentIfDeactivated } = require('./commentHelpers/commentDeactivatedHelper'); +const { preSpace } = require('../utils/preSpace'); +const { getName } = require('./general'); + +const prepareIndexKeys = ({ + indexKeys, + wholeStatementCommented, + isCaseSensitive, + divideIntoActivatedAndDeactivated, +}) => { + const dividedKeys = divideIntoActivatedAndDeactivated(indexKeys || [], key => getName(isCaseSensitive, key.name)); + const commentedKeys = dividedKeys.deactivatedItems.length + ? commentIfDeactivated( + dividedKeys.deactivatedItems.join(', '), + { + isActivated: wholeStatementCommented, + isPartOfLine: true, + }, + true, + ) + : ''; + + return ( + dividedKeys.activatedItems.join(', ') + + (wholeStatementCommented && commentedKeys && dividedKeys.activatedItems.length + ? ', ' + commentedKeys + : preSpace(commentedKeys)) + ); +}; + +module.exports = { + prepareIndexKeys, +}; diff --git a/properties_pane/entity_level/entityLevelConfig.json b/properties_pane/entity_level/entityLevelConfig.json index 25218be9..311da243 100644 --- a/properties_pane/entity_level/entityLevelConfig.json +++ b/properties_pane/entity_level/entityLevelConfig.json @@ -114,7 +114,6 @@ making sure that you maintain a proper JSON format. } */ - [ { "lowerTab": "Details", @@ -162,10 +161,16 @@ making sure that you maintain a proper JSON format. "propertyType": "checkbox", "dependency": { "type": "not", - "values": { - "key": "external", - "value": true - } + "values": [ + { + "key": "external", + "value": true + }, + { + "key": "hybrid", + "value": true + } + ] } }, { @@ -201,6 +206,36 @@ making sure that you maintain a proper JSON format. { "key": "transient", "value": true + }, + { + "key": "hybrid", + "value": true + } + ] + } + }, + { + "propertyName": "Hybrid", + "propertyKeyword": "hybrid", + "propertyType": "checkbox", + "dependency": { + "type": "not", + "values": [ + { + "key": "external", + "value": true + }, + { + "key": "transient", + "value": true + }, + { + "key": "dynamic", + "value": true + }, + { + "key": "iceberg", + "value": true } ] } @@ -584,6 +619,10 @@ making sure that you maintain a proper JSON format. { "key": "external", "value": true + }, + { + "key": "hybrid", + "value": true } ] } @@ -602,6 +641,10 @@ making sure that you maintain a proper JSON format. { "key": "external", "value": true + }, + { + "key": "hybrid", + "value": true } ] } @@ -644,6 +687,10 @@ making sure that you maintain a proper JSON format. { "key": "transient", "value": true + }, + { + "key": "hybrid", + "value": true } ] } @@ -654,10 +701,16 @@ making sure that you maintain a proper JSON format. "propertyType": "group", "dependency": { "type": "not", - "values": { - "key": "external", - "value": true - } + "values": [ + { + "key": "external", + "value": true + }, + { + "key": "hybrid", + "value": true + } + ] }, "structure": [ { @@ -790,7 +843,11 @@ making sure that you maintain a proper JSON format. "ESCAPE_UNENCLOSED_FIELD": "\\", "TRIM_SPACE": false, "FIELD_OPTIONALLY_ENCLOSED_BY": "NONE", - "NULL_IF": [{ "NULL_IF_item": "\\N" }], + "NULL_IF": [ + { + "NULL_IF_item": "\\N" + } + ], "ERROR_ON_COLUMN_COUNT_MISMATCH": true, "VALIDATE_UTF8": true, "EMPTY_FIELD_AS_NULL": true, @@ -1029,7 +1086,11 @@ making sure that you maintain a proper JSON format. ] }, "defaultValue": { - "NULL_IF": [{ "NULL_IF_item": "\\N" }] + "NULL_IF": [ + { + "NULL_IF_item": "\\N" + } + ] }, "structure": [ { @@ -1754,6 +1815,73 @@ making sure that you maintain a proper JSON format. } ] }, + { + "lowerTab": "Indexes", + "structure": [ + { + "propertyName": "Index", + "propertyType": "group", + "propertyKeyword": "Indxs", + "dependency": { + "key": "hybrid", + "value": true + }, + "structure": [ + { + "propertyName": "Name", + "propertyKeyword": "indxName", + "propertyTooltip": "Index name are needed to drop indexes and appear in error messages when a constraint is violated.", + "propertyType": "text", + "validation": { + "required": true + } + }, + { + "propertyName": "Activated", + "propertyKeyword": "isActivated", + "propertyTooltip": "Deactivated item will be not included in FE script", + "propertyType": "checkbox", + "defaultValue": true + }, + { + "propertyName": "Or replace", + "propertyKeyword": "orReplace", + "propertyType": "checkbox", + "disabledOnCondition": { + "key": "ifNotExist", + "value": true + } + }, + { + "propertyName": "If not exist", + "propertyKeyword": "ifNotExist", + "propertyTooltip": "if the specified table already exists, the command should make no changes and return a message that the table exists, rather than terminating with an error. ", + "propertyType": "checkbox", + "disabledOnCondition": { + "key": "orReplace", + "value": true + } + }, + { + "propertyName": "Keys", + "propertyKeyword": "indxKey", + "propertyType": "fieldList", + "template": "orderedList", + "validation": { + "required": true, + "minLength": 1 + } + }, + { + "propertyName": "Include", + "propertyKeyword": "indxIncludeKey", + "propertyType": "fieldList", + "template": "orderedList" + } + ] + } + ] + }, { "lowerTab": "dbt", "system": true,