From 6299d1c278d68e7cdb090d3c9354136830ca2541 Mon Sep 17 00:00:00 2001 From: Sooraj Date: Wed, 14 Oct 2020 13:27:32 +0530 Subject: [PATCH] console: down migrations improvements (close #3503, #4988) (#4790) --- CHANGELOG.md | 1 + .../components/Common/utils/v1QueryUtils.ts | 10 +- .../Services/Actions/Permissions/reducer.js | 2 +- .../Services/Actions/Permissions/utils.js | 36 +- .../components/Services/Actions/ServerIO.js | 94 +- .../Services/Data/Add/AddActions.js | 31 +- .../Data/Add/AddExistingTableViewActions.js | 77 +- .../components/Services/Data/DataActions.js | 6 +- .../Data/Function/customFunctionReducer.js | 109 +-- .../Services/Data/RawSQL/Actions.js | 18 +- .../Services/Data/Schema/Actions.js | 22 +- .../Data/TableModify/ModifyActions.js | 916 +++++------------- .../Services/Data/TablePermissions/Actions.js | 68 +- .../Data/TableRelationships/Actions.js | 168 ++-- console/src/components/Services/Data/utils.js | 2 + .../components/Services/Events/ServerIO.ts | 97 +- .../Services/RemoteSchema/Actions.js | 1 - .../Add/addRemoteSchemaReducer.js | 65 +- .../components/Services/Settings/Actions.js | 12 +- .../src/components/Services/Types/ServerIO.js | 12 +- console/src/types.ts | 11 + console/src/utils/migration/Migration.ts | 40 + console/src/utils/migration/utils.ts | 291 ++++++ 23 files changed, 952 insertions(+), 1137 deletions(-) create mode 100644 console/src/utils/migration/Migration.ts create mode 100644 console/src/utils/migration/utils.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 07dde84cc149e..d4edcbac8b49d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,6 +86,7 @@ This release contains the [PDV refactor (#4111)](https://github.com/hasura/graph - console: remove ONLY as default for ALTER TABLE in column alter operations (close #5512) #5706 - console: add option to flag an insertion as a migration from `Data` section (close #1766) (#4933) - console: add notifications (#5070) +- console: down migrations improvements (close #3503, #4988) (#4790) - docs: add docs page on networking with docker (close #4346) (#4811) - docs: add tabs for console / cli / api workflows (close #3593) (#4948) - docs: add postgres concepts page to docs (close #4440) (#4471) diff --git a/console/src/components/Common/utils/v1QueryUtils.ts b/console/src/components/Common/utils/v1QueryUtils.ts index c50ab759c9a00..ceb99750bf99a 100644 --- a/console/src/components/Common/utils/v1QueryUtils.ts +++ b/console/src/components/Common/utils/v1QueryUtils.ts @@ -59,15 +59,15 @@ export type Header = { export const getRunSqlQuery = ( sql: string, - shouldCascade?: boolean, - readOnly?: boolean + cascade = false, + read_only = false ) => { return { type: 'run_sql', args: { sql: terminateSql(sql), - cascade: !!shouldCascade, - read_only: !!readOnly, + cascade, + read_only, }, }; }; @@ -306,7 +306,7 @@ export const getFetchCustomTypesQuery = () => { }; }; -type CustomRootFields = { +export type CustomRootFields = { select: string; select_by_pk: string; select_aggregate: string; diff --git a/console/src/components/Services/Actions/Permissions/reducer.js b/console/src/components/Services/Actions/Permissions/reducer.js index fdb6bd51391f2..984cdeef38d81 100644 --- a/console/src/components/Services/Actions/Permissions/reducer.js +++ b/console/src/components/Services/Actions/Permissions/reducer.js @@ -32,7 +32,7 @@ export const setDefaults = () => ({ }); const MAKE_REQUEST = 'Actions/Permissions/MAKE_REQUEST'; -export const makeRequest = () => ({ type: MAKE_REQUEST }); +export const makePermRequest = () => ({ type: MAKE_REQUEST }); const REQUEST_SUCCESS = 'Actions/Permissions/REQUEST_SUCCESS'; export const setRequestSuccess = () => ({ type: REQUEST_SUCCESS }); const REQUEST_FAILURE = 'Actions/Permissions/REQUEST_FAILURE'; diff --git a/console/src/components/Services/Actions/Permissions/utils.js b/console/src/components/Services/Actions/Permissions/utils.js index acb63b9ca0ef3..f5836edb6b224 100644 --- a/console/src/components/Services/Actions/Permissions/utils.js +++ b/console/src/components/Services/Actions/Permissions/utils.js @@ -3,50 +3,48 @@ import { getDropActionPermissionQuery, } from '../../../Common/utils/v1QueryUtils'; import { findActionPermission } from '../utils'; +import Migration from '../../../../utils/migration/Migration'; -export const getActionPermissionQueries = ( +export const getActionPermissionMigration = ( permissionEdit, allPermissions, actionName ) => { const { role, newRole, filter } = permissionEdit; - const upQueries = []; - const downQueries = []; - const permRole = (newRole || role).trim(); const existingPerm = findActionPermission(allPermissions, permRole); - + const migration = new Migration(); if (newRole || (!newRole && !existingPerm)) { - upQueries.push( - getCreateActionPermissionQuery( + migration.add( + (getCreateActionPermissionQuery( { role: permRole, filter, }, actionName - ) + ), + getDropActionPermissionQuery(permRole, actionName)) ); - downQueries.push(getDropActionPermissionQuery(permRole, actionName)); } if (existingPerm) { - upQueries.push(getDropActionPermissionQuery(permRole, actionName)); - upQueries.push( - getCreateActionPermissionQuery({ role: permRole, filter }, actionName) + migration.add( + getDropActionPermissionQuery(permRole, actionName), + getDropActionPermissionQuery(permRole, actionName) ); - downQueries.push(getDropActionPermissionQuery(permRole, actionName)); - upQueries.push( + + migration.add( + getCreateActionPermissionQuery({ role: permRole, filter }, actionName) + ); // TODO Down queries + migration.add( getCreateActionPermissionQuery( { role: permRole, filter: existingPerm.definition.select.filter }, actionName ) - ); + ); // TODO Down queries } - return { - upQueries, - downQueries, - }; + return migration; }; diff --git a/console/src/components/Services/Actions/ServerIO.js b/console/src/components/Services/Actions/ServerIO.js index 1d921b31624fc..076d321485a0f 100644 --- a/console/src/components/Services/Actions/ServerIO.js +++ b/console/src/components/Services/Actions/ServerIO.js @@ -53,10 +53,12 @@ import { } from './Modify/reducer'; import { - makeRequest as makePermRequest, + makePermRequest, setRequestSuccess as setPermRequestSuccess, setRequestFailure as setPermRequestFailure, } from './Permissions/reducer'; +import { getActionPermissionMigration } from './Permissions/utils'; +import Migration from '../../../utils/migration/Migration'; import { findAction, getActionPermissions, @@ -64,7 +66,6 @@ import { persistDerivedAction, updatePersistedDerivation, } from './utils'; -import { getActionPermissionQueries } from './Permissions/utils'; export const fetchActions = () => { return (dispatch, getState) => { @@ -177,6 +178,8 @@ export const createAction = () => (dispatch, getState) => { return; } } + // Migration queries start + const migration = new Migration(); const customFieldsQueryUp = generateSetCustomTypesQuery( reformCustomTypes(mergedTypes) @@ -185,6 +188,7 @@ export const createAction = () => (dispatch, getState) => { const customFieldsQueryDown = generateSetCustomTypesQuery( reformCustomTypes(existingTypesList) ); + migration.add(customFieldsQueryUp, customFieldsQueryDown); const actionQueryUp = generateCreateActionQuery( state.name, @@ -194,8 +198,8 @@ export const createAction = () => (dispatch, getState) => { const actionQueryDown = generateDropActionQuery(state.name); - const upQueries = [customFieldsQueryUp, actionQueryUp]; - const downQueries = [actionQueryDown, customFieldsQueryDown]; + migration.add(actionQueryUp, actionQueryDown); + // Migration queries end const migrationName = `create_action_${state.name}`; const requestMsg = 'Creating action...'; @@ -219,8 +223,8 @@ export const createAction = () => (dispatch, getState) => { makeMigrationCall( dispatch, getState, - upQueries, - downQueries, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -325,22 +329,19 @@ export const saveAction = currentAction => (dispatch, getState) => { currentAction.comment ); - let upQueries; - let downQueries; + // Migration queries start + const migration = new Migration(); if (!isActionNameChange) { - upQueries = [customFieldsQueryUp, updateCurrentActionQuery]; - downQueries = [customFieldsQueryDown, rollbackActionQuery]; + migration.add(customFieldsQueryUp, customFieldsQueryDown); + migration.add(updateCurrentActionQuery, rollbackActionQuery); } else { const isOk = getConfirmation( 'You seem to have changed the action name. This will cause the permissions to be dropped.' ); if (!isOk) return; - upQueries = [ - dropCurrentActionQuery, - customFieldsQueryUp, - createNewActionQuery, - ]; - downQueries = [actionQueryDown, customFieldsQueryDown, oldActionQueryUp]; + migration.add(dropCurrentActionQuery, oldActionQueryUp); + migration.add(customFieldsQueryUp, customFieldsQueryDown); + migration.add(createNewActionQuery, actionQueryDown); } const migrationName = `modify_action_${currentAction.action_name}_to_${state.name}`; @@ -368,8 +369,8 @@ export const saveAction = currentAction => (dispatch, getState) => { makeMigrationCall( dispatch, getState, - upQueries, - downQueries, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -385,11 +386,17 @@ export const deleteAction = currentAction => (dispatch, getState) => { if (!isOk) { return; } - const upQuery = generateDropActionQuery(currentAction.action_name); - const downQuery = generateCreateActionQuery( - currentAction.action_name, - currentAction.action_defn, - currentAction.comment + + // Migration queries start + const migration = new Migration(); + + migration.add( + generateDropActionQuery(currentAction.action_name), + generateCreateActionQuery( + currentAction.action_name, + currentAction.action_defn, + currentAction.comment + ) ); const migrationName = `delete_action_${currentAction.action_name}`; @@ -410,8 +417,8 @@ export const deleteAction = currentAction => (dispatch, getState) => { makeMigrationCall( dispatch, getState, - [upQuery], - [downQuery], + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -478,9 +485,9 @@ export const addActionRel = (relConfig, successCb, existingRelConfig) => ( const customTypesQueryDown = generateSetCustomTypesQuery( reformCustomTypes(existingTypes) ); - - const upQueries = [customTypesQueryUp]; - const downQueries = [customTypesQueryDown]; + // Migration queries start + const migration = new Migration(); + migration.add(customTypesQueryUp, customTypesQueryDown); const migrationName = `save_rel_${relConfig.name}_on_${relConfig.typename}`; const requestMsg = 'Saving relationship...'; @@ -500,8 +507,8 @@ export const addActionRel = (relConfig, successCb, existingRelConfig) => ( makeMigrationCall( dispatch, getState, - upQueries, - downQueries, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -537,9 +544,8 @@ export const removeActionRel = (relName, typename, successCb) => ( const customTypesQueryDown = generateSetCustomTypesQuery( reformCustomTypes(existingTypes) ); - - const upQueries = [customTypesQueryUp]; - const downQueries = [customTypesQueryDown]; + const migration = new Migration(); + migration.add(customTypesQueryUp, customTypesQueryDown); const migrationName = 'remove_action_rel'; // TODO: better migration name const requestMsg = 'Removing relationship...'; @@ -560,8 +566,8 @@ export const removeActionRel = (relName, typename, successCb) => ( makeMigrationCall( dispatch, getState, - upQueries, - downQueries, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -584,7 +590,7 @@ export const saveActionPermission = (successCb, errorCb) => ( findAction(allActions, currentAction) ); - const { upQueries, downQueries } = getActionPermissionQueries( + const migration = getActionPermissionMigration( permissionEdit, allPermissions, currentAction @@ -614,8 +620,8 @@ export const saveActionPermission = (successCb, errorCb) => ( makeMigrationCall( dispatch, getState, - upQueries, - downQueries, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -639,10 +645,10 @@ export const removeActionPermission = (successCb, errorCb) => ( const { role, filter } = permissionEdit; - const upQuery = getDropActionPermissionQuery(role, currentAction); - const downQuery = getCreateActionPermissionQuery( - { role, filter }, - currentAction + const migration = new Migration(); + migration.add( + getDropActionPermissionQuery(role, currentAction), + getCreateActionPermissionQuery({ role, filter }, currentAction) ); const migrationName = 'removing_action_perm'; @@ -669,8 +675,8 @@ export const removeActionPermission = (successCb, errorCb) => ( makeMigrationCall( dispatch, getState, - [upQuery], - [downQuery], + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, diff --git a/console/src/components/Services/Data/Add/AddActions.js b/console/src/components/Services/Data/Add/AddActions.js index 4700e8c93f04d..140f5c77fea88 100644 --- a/console/src/components/Services/Data/Add/AddActions.js +++ b/console/src/components/Services/Data/Add/AddActions.js @@ -13,6 +13,7 @@ import { isColTypeString, isPostgresFunction } from '../utils'; import { sqlEscapeText } from '../../../Common/utils/sqlUtils'; import { getRunSqlQuery } from '../../../Common/utils/v1QueryUtils'; import { getTableModifyRoute } from '../../../Common/utils/routesUtils'; +import Migration from '../../../../utils/migration/Migration'; const SET_DEFAULTS = 'AddTable/SET_DEFAULTS'; const SET_TABLENAME = 'AddTable/SET_TABLENAME'; @@ -334,19 +335,19 @@ const createTableSql = () => { // apply migrations const migrationName = 'create_table_' + currentSchema + '_' + tableName; + const sqlDropTable = + 'DROP TABLE ' + '"' + currentSchema + '"' + '.' + '"' + tableName + '"'; - // up migration - const upQueryArgs = []; + const migration = new Migration(); if (hasUUIDDefault) { const sqlCreateExtension = 'CREATE EXTENSION IF NOT EXISTS pgcrypto;'; - - upQueryArgs.push(getRunSqlQuery(sqlCreateExtension)); + migration.add(getRunSqlQuery(sqlCreateExtension)); } - upQueryArgs.push(getRunSqlQuery(sqlCreateTable)); + migration.add(getRunSqlQuery(sqlCreateTable), getRunSqlQuery(sqlDropTable)); - upQueryArgs.push({ + migration.add({ type: 'add_existing_table_or_view', args: { name: tableName, @@ -354,20 +355,6 @@ const createTableSql = () => { }, }); - const upQuery = { - type: 'bulk', - args: upQueryArgs, - }; - - // down migration - const sqlDropTable = - 'DROP TABLE ' + '"' + currentSchema + '"' + '.' + '"' + tableName + '"'; - - const downQuery = { - type: 'bulk', - args: [getRunSqlQuery(sqlDropTable)], - }; - // make request const requestMsg = 'Creating table...'; const successMsg = 'Table Created'; @@ -391,8 +378,8 @@ const createTableSql = () => { makeMigrationCall( dispatch, getState, - upQuery.args, - downQuery.args, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, diff --git a/console/src/components/Services/Data/Add/AddExistingTableViewActions.js b/console/src/components/Services/Data/Add/AddExistingTableViewActions.js index b9659d84a7d07..3bb493e65ae42 100644 --- a/console/src/components/Services/Data/Add/AddExistingTableViewActions.js +++ b/console/src/components/Services/Data/Add/AddExistingTableViewActions.js @@ -13,6 +13,7 @@ import { getFunctionModifyRoute, } from '../../../Common/utils/routesUtils'; import { checkIfTable } from '../../../Common/utils/pgUtils'; +import Migration from '../../../../utils/migration/Migration'; const SET_DEFAULTS = 'AddExistingTable/SET_DEFAULTS'; const SET_TABLENAME = 'AddExistingTable/SET_TABLENAME'; @@ -47,16 +48,11 @@ const addExistingTableSql = () => { }, }, }; + + const migration = new Migration(); + migration.add(requestBodyUp, requestBodyDown); const migrationName = 'add_existing_table_or_view_' + currentSchema + '_' + tableName; - const upQuery = { - type: 'bulk', - args: [requestBodyUp], - }; - const downQuery = { - type: 'bulk', - args: [requestBodyDown], - }; const requestMsg = 'Adding existing table/view...'; const successMsg = 'Existing table/view added'; @@ -82,8 +78,8 @@ const addExistingTableSql = () => { makeMigrationCall( dispatch, getState, - upQuery.args, - downQuery.args, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -100,29 +96,24 @@ const addExistingFunction = name => { dispatch(showSuccessNotification('Adding an function...')); const currentSchema = getState().tables.currentSchema; - const requestBodyUp = { + const upMigration = { type: 'track_function', args: { name, schema: currentSchema, }, }; - const requestBodyDown = { + const downMigration = { type: 'untrack_function', args: { name, schema: currentSchema, }, }; + const migration = new Migration(); + migration.add(upMigration, downMigration); + const migrationName = 'add_existing_function ' + currentSchema + '_' + name; - const upQuery = { - type: 'bulk', - args: [requestBodyUp], - }; - const downQuery = { - type: 'bulk', - args: [requestBodyDown], - }; const requestMsg = 'Adding existing function...'; const successMsg = 'Existing function added'; @@ -141,8 +132,8 @@ const addExistingFunction = name => { makeMigrationCall( dispatch, getState, - upQuery.args, - downQuery.args, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -159,37 +150,31 @@ const addAllUntrackedTablesSql = tableList => { dispatch({ type: MAKING_REQUEST }); dispatch(showSuccessNotification('Existing table/view added!')); - const bulkQueryUp = []; - const bulkQueryDown = []; + + const migration = new Migration(); for (let i = 0; i < tableList.length; i++) { if (tableList[i].table_name !== 'schema_migrations') { - bulkQueryUp.push({ - type: 'add_existing_table_or_view', - args: { - name: tableList[i].table_name, - schema: currentSchema, - }, - }); - bulkQueryDown.push({ - type: 'untrack_table', - args: { - table: { + migration.add( + { + type: 'add_existing_table_or_view', + args: { name: tableList[i].table_name, schema: currentSchema, }, }, - }); + { + type: 'untrack_table', + args: { + table: { + name: tableList[i].table_name, + schema: currentSchema, + }, + }, + } + ); } } const migrationName = 'add_all_existing_table_or_view_' + currentSchema; - const upQuery = { - type: 'bulk', - args: bulkQueryUp, - }; - const downQuery = { - type: 'bulk', - args: bulkQueryDown, - }; const requestMsg = 'Adding existing table/view...'; const successMsg = 'Existing table/view added'; @@ -209,8 +194,8 @@ const addAllUntrackedTablesSql = tableList => { makeMigrationCall( dispatch, getState, - upQuery.args, - downQuery.args, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, diff --git a/console/src/components/Services/Data/DataActions.js b/console/src/components/Services/Data/DataActions.js index 63334b8283241..0495a5de7d8b2 100644 --- a/console/src/components/Services/Data/DataActions.js +++ b/console/src/components/Services/Data/DataActions.js @@ -43,6 +43,7 @@ import { fetchColumnTypesQuery, fetchColumnDefaultFunctions } from './utils'; import { fetchColumnCastsQuery, convertArrayToJson } from './TableModify/utils'; import { CLI_CONSOLE_MODE, SERVER_CONSOLE_MODE } from '../../../constants'; +import { getDownQueryComments } from '../../../utils/migration/utils'; import { isEmpty } from '../../Common/utils/jsUtils'; const SET_TABLE = 'Data/SET_TABLE'; @@ -601,7 +602,7 @@ const makeMigrationCall = ( dispatch, getState, upQueries, - downQueries, + downQueries = [], migrationName, customOnSuccess, customOnError, @@ -619,7 +620,8 @@ const makeMigrationCall = ( const downQuery = { type: 'bulk', - args: downQueries, + args: + downQueries.length > 0 ? downQueries : getDownQueryComments(upQueries), }; const migrationBody = { diff --git a/console/src/components/Services/Data/Function/customFunctionReducer.js b/console/src/components/Services/Data/Function/customFunctionReducer.js index 8aeab7855f46e..0fa9875320eac 100644 --- a/console/src/components/Services/Data/Function/customFunctionReducer.js +++ b/console/src/components/Services/Data/Function/customFunctionReducer.js @@ -7,20 +7,13 @@ import Endpoints, { globalCookiePolicy } from '../../../../Endpoints'; import requestAction from '../../../../utils/requestAction'; import dataHeaders from '../Common/Headers'; -import globals from '../../../../Globals'; - -import returnMigrateUrl from '../Common/getMigrateUrl'; -import { CLI_CONSOLE_MODE, SERVER_CONSOLE_MODE } from '../../../../constants'; -import { loadMigrationStatus } from '../../../Main/Actions'; -import { handleMigrationErrors } from '../../../../utils/migration'; - -import { showSuccessNotification } from '../../Common/Notification'; - import { fetchTrackedFunctions } from '../DataActions'; import _push from '../push'; import { getSchemaBaseRoute } from '../../../Common/utils/routesUtils'; import { getRunSqlQuery } from '../../../Common/utils/v1QueryUtils'; +import { makeRequest } from '../../RemoteSchema/Actions'; +import Migration from '../../../../utils/migration/Migration'; /* Constants */ @@ -47,71 +40,6 @@ const SESSVAR_CUSTOM_FUNCTION_ADD_SUCCESS = /* */ -const makeRequest = ( - upQueries, - downQueries, - migrationName, - customOnSuccess, - customOnError, - requestMsg, - successMsg, - errorMsg -) => { - return (dispatch, getState) => { - const upQuery = { - type: 'bulk', - args: upQueries, - }; - - const downQuery = { - type: 'bulk', - args: downQueries, - }; - - const migrationBody = { - name: migrationName, - up: upQuery.args, - down: downQuery.args, - }; - - const currMigrationMode = getState().main.migrationMode; - - const migrateUrl = returnMigrateUrl(currMigrationMode); - - let finalReqBody; - if (globals.consoleMode === SERVER_CONSOLE_MODE) { - finalReqBody = upQuery; - } else if (globals.consoleMode === CLI_CONSOLE_MODE) { - finalReqBody = migrationBody; - } - const url = migrateUrl; - const options = { - method: 'POST', - credentials: globalCookiePolicy, - headers: dataHeaders(getState), - body: JSON.stringify(finalReqBody), - }; - - const onSuccess = data => { - if (globals.consoleMode === CLI_CONSOLE_MODE) { - dispatch(loadMigrationStatus()); // don't call for server mode - } - if (successMsg) { - dispatch(showSuccessNotification(successMsg)); - } - customOnSuccess(data); - }; - - const onError = err => { - dispatch(handleMigrationErrors(errorMsg, err)); - customOnError(err); - }; - - dispatch(showSuccessNotification(requestMsg)); - return dispatch(requestAction(url, options)).then(onSuccess, onError); - }; -}; - /* Action creators */ const fetchCustomFunction = (functionName, schema) => { return (dispatch, getState) => { @@ -201,11 +129,15 @@ const deleteFunctionSql = () => { const sqlDropFunction = 'DROP FUNCTION ' + functionNameWithSchema + functionArgString; - const sqlUpQueries = [getRunSqlQuery(sqlDropFunction)]; + const migration = new Migration(); - const sqlDownQueries = []; if (functionDefinition && functionDefinition.length > 0) { - sqlDownQueries.push(getRunSqlQuery(functionDefinition)); + migration.add( + getRunSqlQuery(sqlDropFunction), + getRunSqlQuery(functionDefinition) + ); + } else { + migration.add(getRunSqlQuery(sqlDropFunction)); // TODO Down queries } // Apply migrations @@ -225,8 +157,8 @@ const deleteFunctionSql = () => { dispatch({ type: DELETING_CUSTOM_FUNCTION }); return dispatch( makeRequest( - sqlUpQueries, - sqlDownQueries, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -264,18 +196,9 @@ const unTrackCustomFunction = () => { }, }; - const upQueryArgs = []; - upQueryArgs.push(payload); - const downQueryArgs = []; - downQueryArgs.push(downPayload); - const upQuery = { - type: 'bulk', - args: upQueryArgs, - }; - const downQuery = { - type: 'bulk', - args: downQueryArgs, - }; + const migration = new Migration(); + migration.add(payload, downPayload); + const requestMsg = 'Deleting custom function...'; const successMsg = 'Custom function deleted successfully'; const errorMsg = 'Delete custom function failed'; @@ -294,8 +217,8 @@ const unTrackCustomFunction = () => { dispatch({ type: UNTRACKING_CUSTOM_FUNCTION }); return dispatch( makeRequest( - upQuery.args, - downQuery.args, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, diff --git a/console/src/components/Services/Data/RawSQL/Actions.js b/console/src/components/Services/Data/RawSQL/Actions.js index 9ef8d758d9833..b8d7ed46092c4 100644 --- a/console/src/components/Services/Data/RawSQL/Actions.js +++ b/console/src/components/Services/Data/RawSQL/Actions.js @@ -18,6 +18,7 @@ import dataHeaders from '../Common/Headers'; import returnMigrateUrl from '../Common/getMigrateUrl'; import { getRunSqlQuery } from '../../../Common/utils/v1QueryUtils'; import requestAction from '../../../../utils/requestAction'; +import { getDownQueryComments } from '../../../../utils/migration/utils'; const MAKING_REQUEST = 'RawSQL/MAKING_REQUEST'; const SET_SQL = 'RawSQL/SET_SQL'; @@ -49,6 +50,7 @@ const executeSQL = (isMigration, migrationName, statementTimeout) => ( let url = Endpoints.rawSQL; const schemaChangesUp = []; + let schemaChangesDown = []; if (isStatementTimeout) { schemaChangesUp.push( @@ -60,7 +62,9 @@ const executeSQL = (isMigration, migrationName, statementTimeout) => ( ); } - schemaChangesUp.push(getRunSqlQuery(sql, isCascadeChecked, readOnlyMode)); + const runSQLQuery = getRunSqlQuery(sql, isCascadeChecked, readOnlyMode); + schemaChangesUp.push(runSQLQuery); + schemaChangesDown = [...getDownQueryComments([runSQLQuery])]; if (isTableTrackChecked) { const objects = parseCreateSQL(sql); @@ -70,17 +74,27 @@ const executeSQL = (isMigration, migrationName, statementTimeout) => ( type: '', args: {}, }; + const unTrackQuery = { + type: '', + args: {}, + }; if (object.type === 'function') { trackQuery.type = 'track_function'; + unTrackQuery.type = 'untrack_function'; } else { trackQuery.type = 'add_existing_table_or_view'; + unTrackQuery.type = 'untrack_table'; } trackQuery.args.name = object.name; trackQuery.args.schema = object.schema; + unTrackQuery.args.name = object.name; + unTrackQuery.args.schema = object.schema; + schemaChangesUp.push(trackQuery); + schemaChangesDown.unshift(unTrackQuery); }); } @@ -94,7 +108,7 @@ const executeSQL = (isMigration, migrationName, statementTimeout) => ( requestBody = { name: migrationName, up: schemaChangesUp, - down: [], + down: schemaChangesDown, }; } const options = { diff --git a/console/src/components/Services/Data/Schema/Actions.js b/console/src/components/Services/Data/Schema/Actions.js index fb09fc54cc017..e5910fa3c5c42 100644 --- a/console/src/components/Services/Data/Schema/Actions.js +++ b/console/src/components/Services/Data/Schema/Actions.js @@ -3,6 +3,7 @@ import { showErrorNotification } from '../../Common/Notification'; import { makeMigrationCall, fetchSchemaList } from '../DataActions'; import { getConfirmation } from '../../../Common/utils/jsUtils'; import { getRunSqlQuery } from '../../../Common/utils/v1QueryUtils'; +import Migration from '../../../../utils/migration/Migration'; const getDropSchemaSql = schemaName => `drop schema "${schemaName}" cascade;`; @@ -19,10 +20,11 @@ export const createNewSchema = (schemaName, successCb, errorCb) => { ) ); } - - const migrationUp = [getRunSqlQuery(getCreateSchemaSql(schemaName))]; - - const migrationDown = [getRunSqlQuery(getDropSchemaSql(schemaName))]; + const migration = new Migration(); + migration.add( + getRunSqlQuery(getCreateSchemaSql(schemaName)), + getRunSqlQuery(getDropSchemaSql(schemaName)) + ); const migrationName = `create_schema_${schemaName}`; const requestMsg = 'Creating schema'; @@ -45,8 +47,8 @@ export const createNewSchema = (schemaName, successCb, errorCb) => { makeMigrationCall( dispatch, getState, - migrationUp, - migrationDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -72,8 +74,8 @@ export const deleteCurrentSchema = (successCb, errorCb) => { if (!isOk) { return; } - - const migrationUp = [getRunSqlQuery(getDropSchemaSql(currentSchema))]; + const migration = new Migration(); + migration.add(getRunSqlQuery(getDropSchemaSql(currentSchema))); const migrationName = `drop_schema_${currentSchema}`; const requestMsg = 'Dropping schema'; const successMsg = 'Successfully dropped schema'; @@ -94,8 +96,8 @@ export const deleteCurrentSchema = (successCb, errorCb) => { makeMigrationCall( dispatch, getState, - migrationUp, - [], + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, diff --git a/console/src/components/Services/Data/TableModify/ModifyActions.js b/console/src/components/Services/Data/TableModify/ModifyActions.js index a191506ba44ea..2c633929d3365 100644 --- a/console/src/components/Services/Data/TableModify/ModifyActions.js +++ b/console/src/components/Services/Data/TableModify/ModifyActions.js @@ -64,6 +64,8 @@ import { getSchemaBaseRoute, getTableModifyRoute, } from '../../../Common/utils/routesUtils'; +import { getColumnUpdateMigration } from '../../../../utils/migration/utils'; +import Migration from '../../../../utils/migration/Migration'; const DELETE_PK_WARNING = 'Without a primary key there is no way to uniquely identify a row of a table'; @@ -176,45 +178,37 @@ export const saveComputedField = ( originalComputedField, successCb ) => (dispatch, getState) => { - const migrationUp = []; - const migrationDown = []; - + const migration = new Migration(); const tableDef = getTableDef(table); const computedFieldName = getComputedFieldName(computedField); if (originalComputedField) { - migrationUp.push( + migration.add( getDropComputedFieldQuery( tableDef, getComputedFieldName(originalComputedField) + ), + getAddComputedFieldQuery( + tableDef, + getComputedFieldName(originalComputedField), + originalComputedField.definition, + originalComputedField.comment ) ); } - migrationUp.push( + migration.add( getAddComputedFieldQuery( tableDef, computedFieldName, computedField.definition, computedField.comment - ) + ), + getDropComputedFieldQuery(tableDef, computedFieldName) ); - migrationDown.push(getDropComputedFieldQuery(tableDef, computedFieldName)); - - if (originalComputedField) { - migrationDown.push( - getAddComputedFieldQuery( - tableDef, - getComputedFieldName(originalComputedField), - originalComputedField.definition, - originalComputedField.comment - ) - ); - } - - const migrationName = `save_computed_field_${computedField.table_schema}_${computedField.table_name}_${computedFieldName}`; + const migrationName = `save_computed_field_${table.table_schema}_${table.table_name}_${computedFieldName}`; const requestMsg = 'Saving computed field...'; const successMsg = 'Saving computed field successful'; const errorMsg = 'Saving computed field failed'; @@ -226,8 +220,8 @@ export const saveComputedField = ( makeMigrationCall( dispatch, getState, - migrationUp, - migrationDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -241,15 +235,11 @@ export const deleteComputedField = (computedField, table) => ( dispatch, getState ) => { - const migrationUp = []; - const migrationDown = []; - const tableDef = getTableDef(table); const computedFieldName = getComputedFieldName(computedField); - - migrationUp.push(getDropComputedFieldQuery(tableDef, computedFieldName)); - - migrationDown.push( + const migration = new Migration(); + migration.add( + getDropComputedFieldQuery(tableDef, computedFieldName), getAddComputedFieldQuery( tableDef, computedFieldName, @@ -268,8 +258,8 @@ export const deleteComputedField = (computedField, table) => ( makeMigrationCall( dispatch, getState, - migrationUp, - migrationDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -295,16 +285,19 @@ export const setCustomRootFields = successCb => (dispatch, getState) => { const existingRootFields = getTableCustomRootFields(table); const existingCustomColumnNames = getTableCustomColumnNames(table); + const migration = new Migration(); - const upQuery = getSetCustomRootFieldsQuery( - tableDef, - sanitiseRootFields(newRootFields), - existingCustomColumnNames - ); - const downQuery = getSetCustomRootFieldsQuery( - tableDef, - existingRootFields, - existingCustomColumnNames + migration.add( + getSetCustomRootFieldsQuery( + tableDef, + sanitiseRootFields(newRootFields), + existingCustomColumnNames + ), + getSetCustomRootFieldsQuery( + tableDef, + existingRootFields, + existingCustomColumnNames + ) ); const migrationName = `set_custom_root_fields_${schemaName}_${tableName}`; @@ -323,8 +316,8 @@ export const setCustomRootFields = successCb => (dispatch, getState) => { makeMigrationCall( dispatch, getState, - [upQuery], - [downQuery], + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -353,16 +346,18 @@ export const removeCheckConstraint = (constraintName, successCb, errorCb) => ( getTableCheckConstraints(table), constraintName ); - - const upQuery = getRunSqlQuery( - getDropConstraintSql(tableName, currentSchema, constraintName) - ); - const downQuery = getRunSqlQuery( - getCreateCheckConstraintSql( - tableName, - currentSchema, - constraintName, - constraint.check + const migration = new Migration(); + migration.add( + getRunSqlQuery( + getDropConstraintSql(tableName, currentSchema, constraintName) + ), + getRunSqlQuery( + getCreateCheckConstraintSql( + tableName, + currentSchema, + constraintName, + constraint.check + ) ) ); @@ -384,8 +379,8 @@ export const removeCheckConstraint = (constraintName, successCb, errorCb) => ( makeMigrationCall( dispatch, getState, - [upQuery], - [downQuery], + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -433,51 +428,39 @@ const savePrimaryKeys = (tableName, schemaName, constraintName) => { if (!changeDetected) { return dispatch(showSuccessNotification('No changes')); } - - const migrationUp = []; + const migration = new Migration(); // skip dropping existing constraint if there is none if (constraintName) { - migrationUp.push( - getRunSqlQuery(getDropPkSql({ schemaName, tableName, constraintName })) - ); - } - // skip creating a new config if no columns were selected - if (numSelectedPkColumns) { - migrationUp.push( + migration.add( + getRunSqlQuery(getDropPkSql({ schemaName, tableName, constraintName })), getRunSqlQuery( getCreatePkSql({ schemaName, tableName, - selectedPkColumns, - constraintName: `${tableName}_pkey`, + selectedPkColumns: tableSchema.primary_key.columns, + constraintName, }) ) ); } - - const migrationDown = []; - // skip dropping in down migration if no constraint was created + // skip creating a new config if no columns were selected if (numSelectedPkColumns) { - migrationDown.push( + migration.add( getRunSqlQuery( - getDropPkSql({ + getCreatePkSql({ schemaName, tableName, + selectedPkColumns, constraintName: `${tableName}_pkey`, }) - ) - ); - } + ), + // skip dropping in down migration if no constraint was created - // skip creating in down migration if no constraint was dropped in up migration - if (constraintName) { - migrationDown.push( getRunSqlQuery( - getCreatePkSql({ + getDropPkSql({ schemaName, tableName, - selectedPkColumns: tableSchema.primary_key.columns, - constraintName, + constraintName: `${tableName}_pkey`, }) ) ); @@ -496,8 +479,8 @@ const savePrimaryKeys = (tableName, schemaName, constraintName) => { makeMigrationCall( dispatch, getState, - migrationUp, - migrationDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -544,14 +527,13 @@ const saveForeignKeys = (index, tableSchema, columns) => { const lcols = filteredMappings.map(cm => `"${columns[cm.column].name}"`); const rcols = filteredMappings.map(cm => `"${cm.refColumn}"`); - const migrationUp = []; const generatedConstraintName = generateFKConstraintName( tableName, lcols, tableSchema.foreign_key_constraints, [constraintName] ); - + const migration = new Migration(); if (constraintName) { // foreign key already exists, alter the foreign key const migrationUpAlterFKeySql = ` @@ -561,7 +543,28 @@ const saveForeignKeys = (index, tableSchema, columns) => { references "${refSchemaName}"."${refTableName}" (${rcols.join(', ')}) on update ${onUpdate} on delete ${onDelete}; `; - migrationUp.push(getRunSqlQuery(migrationUpAlterFKeySql)); + + const oldConstraint = tableSchema.foreign_key_constraints[index]; + const migrationDownAlterFKeySql = ` + alter table "${schemaName}"."${tableName}" drop constraint "${generatedConstraintName}", + add constraint "${constraintName}" + foreign key (${Object.keys(oldConstraint.column_mapping) + .map(lc => `"${lc}"`) + .join(', ')}) + references "${oldConstraint.ref_table_table_schema}"."${ + oldConstraint.ref_table + }" + (${Object.values(oldConstraint.column_mapping) + .map(rc => `"${rc}"`) + .join(', ')}) + on update ${pgConfTypes[oldConstraint.on_update]} + on delete ${pgConfTypes[oldConstraint.on_delete]}; + `; + + migration.add( + getRunSqlQuery(migrationUpAlterFKeySql), + getRunSqlQuery(migrationDownAlterFKeySql) + ); } else { // foreign key not found, create a new one const migrationUpCreateFKeySql = ` @@ -571,39 +574,15 @@ const saveForeignKeys = (index, tableSchema, columns) => { references "${refSchemaName}"."${refTableName}" (${rcols.join(', ')}) on update ${onUpdate} on delete ${onDelete}; `; - - migrationUp.push(getRunSqlQuery(migrationUpCreateFKeySql)); - } - - const migrationDown = []; - - if (constraintName) { - // when foreign key is altered - const oldConstraint = tableSchema.foreign_key_constraints[index]; - const migrationDownAlterFKeySql = ` - alter table "${schemaName}"."${tableName}" drop constraint "${generatedConstraintName}", - add constraint "${constraintName}" - foreign key (${Object.keys(oldConstraint.column_mapping) - .map(lc => `"${lc}"`) - .join(', ')}) - references "${oldConstraint.ref_table_table_schema}"."${ - oldConstraint.ref_table - }" - (${Object.values(oldConstraint.column_mapping) - .map(rc => `"${rc}"`) - .join(', ')}) - on update ${pgConfTypes[oldConstraint.on_update]} - on delete ${pgConfTypes[oldConstraint.on_delete]}; - `; - - migrationDown.push(getRunSqlQuery(migrationDownAlterFKeySql)); - } else { // when foreign key is created const migrationDownDeleteFKeySql = ` - alter table "${schemaName}"."${tableName}" drop constraint "${generatedConstraintName}" + alter table "${schemaName}"."${tableName}" drop constraint "${generatedConstraintName}" `; - migrationDown.push(getRunSqlQuery(migrationDownDeleteFKeySql)); + migration.add( + getRunSqlQuery(migrationUpCreateFKeySql), + getRunSqlQuery(migrationDownDeleteFKeySql) + ); } const migrationName = `set_fk_${schemaName}_${tableName}_${lcols.join( @@ -645,8 +624,8 @@ const saveForeignKeys = (index, tableSchema, columns) => { makeMigrationCall( dispatch, getState, - migrationUp, - migrationDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -674,8 +653,10 @@ const removeForeignKey = (index, tableSchema) => { .join(', ')}) on update ${ pgConfTypes[oldConstraint.on_update] } on delete ${pgConfTypes[oldConstraint.on_delete]};`; - const migrationUp = [getRunSqlQuery(upSql)]; - const migrationDown = [getRunSqlQuery(downSql)]; + + const migration = new Migration(); + migration.add(getRunSqlQuery(upSql), getRunSqlQuery(downSql)); + const migrationName = `delete_fk_${schemaName}_${tableName}_${oldConstraint.constraint_name}`; const requestMsg = 'Deleting foreign key...'; const successMsg = 'Foreign key deleted'; @@ -696,8 +677,8 @@ const removeForeignKey = (index, tableSchema) => { makeMigrationCall( dispatch, getState, - migrationUp, - migrationDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -747,8 +728,11 @@ const changeTableName = (oldName, newName, isTable, tableType) => { const currentSchema = getState().tables.currentSchema; const upSql = `alter ${property} "${currentSchema}"."${oldName}" rename to "${newName}";`; const downSql = `alter ${property} "${currentSchema}"."${newName}" rename to "${oldName}";`; - const migrateUp = [getRunSqlQuery(upSql)]; - const migrateDown = [getRunSqlQuery(downSql)]; + + // Migration + const migration = new Migration(); + migration.add(getRunSqlQuery(upSql), getRunSqlQuery(downSql)); + // apply migrations const migrationName = `rename_${compositeName}_` + currentSchema + '_' + oldName; @@ -769,8 +753,8 @@ const changeTableName = (oldName, newName, isTable, tableType) => { makeMigrationCall( dispatch, getState, - migrateUp, - migrateDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -791,8 +775,6 @@ const deleteTrigger = (trigger, table) => { const upMigrationSql = `DROP TRIGGER "${triggerName}" ON "${tableSchema}"."${tableName}";`; - const migrationUp = [getRunSqlQuery(upMigrationSql)]; - let downMigrationSql = ''; downMigrationSql += `CREATE TRIGGER "${triggerName}" @@ -803,7 +785,12 @@ FOR EACH ${trigger.action_orientation} ${trigger.action_statement};`; downMigrationSql += `COMMENT ON TRIGGER "${triggerName}" ON "${tableSchema}"."${tableName}" IS ${sqlEscapeText(trigger.comment)};`; } - const migrationDown = [getRunSqlQuery(downMigrationSql)]; + + const migration = new Migration(); + migration.add( + getRunSqlQuery(upMigrationSql), + getRunSqlQuery(downMigrationSql) + ); const migrationName = `delete_trigger_${triggerSchema}_${triggerName}`; @@ -819,8 +806,8 @@ IS ${sqlEscapeText(trigger.comment)};`; makeMigrationCall( dispatch, getState, - migrationUp, - migrationDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -837,7 +824,9 @@ const deleteTableSql = tableName => { // handle no primary key const sqlDropTable = 'DROP TABLE ' + '"' + currentSchema + '"' + '.' + '"' + tableName + '"'; - const sqlUpQueries = [getRunSqlQuery(sqlDropTable)]; + + const migration = new Migration(); + migration.add(getRunSqlQuery(sqlDropTable)); // apply migrations const migrationName = 'drop_table_' + currentSchema + '_' + tableName; @@ -858,8 +847,8 @@ const deleteTableSql = tableName => { makeMigrationCall( dispatch, getState, - sqlUpQueries, - [], + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -875,8 +864,8 @@ const untrackTableSql = tableName => { return (dispatch, getState) => { const currentSchema = getState().tables.currentSchema; const tableDef = generateTableDef(tableName, currentSchema); - const upQueries = [getUntrackTableQuery(tableDef)]; - const downQueries = [getTrackTableQuery(tableDef)]; + const migration = new Migration(); + migration.add(getUntrackTableQuery(tableDef), getTrackTableQuery(tableDef)); // apply migrations const migrationName = 'untrack_table_' + currentSchema + '_' + tableName; @@ -895,8 +884,8 @@ const untrackTableSql = tableName => { makeMigrationCall( dispatch, getState, - upQueries, - downQueries, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -981,7 +970,9 @@ const deleteViewSql = (viewName, viewType) => { const currentSchema = getState().tables.currentSchema; const property = viewType.toLowerCase(); const sqlDropView = `DROP ${viewType} "${currentSchema}"."${viewName}"`; - const sqlUpQueries = [getRunSqlQuery(sqlDropView)]; + + const migration = new Migration(); + migration.add(getRunSqlQuery(sqlDropView)); // TODO Down migrations // const sqlCreateView = ''; //pending // const sqlDownQueries = [ // { @@ -1005,8 +996,8 @@ const deleteViewSql = (viewName, viewType) => { makeMigrationCall( dispatch, getState, - sqlUpQueries, - [], + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -1043,32 +1034,24 @@ const deleteColumnSql = (column, tableSchema) => { const alterStatement = 'ALTER TABLE ' + '"' + currentSchema + '"' + '.' + '"' + tableName + '" '; - const schemaChangesUp = [ + const migration = new Migration(); + migration.add( getRunSqlQuery( alterStatement + 'DROP COLUMN ' + '"' + name + '" CASCADE' ), - ]; - const schemaChangesDown = []; - - schemaChangesDown.push( getRunSqlQuery( alterStatement + 'ADD COLUMN ' + '"' + name + '"' + ' ' + col_type ) ); - if (is_nullable) { - schemaChangesDown.push( - getRunSqlQuery( - alterStatement + 'ALTER COLUMN ' + '"' + name + '" ' + 'DROP NOT NULL' - ) - ); - } else { - schemaChangesDown.push( - getRunSqlQuery( - alterStatement + 'ALTER COLUMN ' + '"' + name + '" ' + 'SET NOT NULL' - ) - ); - } + migration.UNSAFE_add( + null, + getRunSqlQuery( + alterStatement + 'ALTER COLUMN ' + '"' + name + '" ' + is_nullable + ? 'DROP NOT NULL' + : 'SET NOT NULL' + ) + ); const merged_fkc = foreign_key_constraints.concat( opp_foreign_key_constraints @@ -1080,7 +1063,8 @@ const deleteColumnSql = (column, tableSchema) => { const rcol = Object.values(fkc.column_mapping); const onUpdate = pgConfTypes[fkc.on_update]; const onDelete = pgConfTypes[fkc.on_delete]; - schemaChangesDown.push( + migration.UNSAFE_add( + null, getRunSqlQuery( alterStatement + 'ADD CONSTRAINT ' + @@ -1100,7 +1084,8 @@ const deleteColumnSql = (column, tableSchema) => { if (unique_constraints.length > 0) { unique_constraints.forEach(uc => { // add unique constraint to down migration - schemaChangesDown.push( + migration.UNSAFE_add( + null, getRunSqlQuery( alterStatement + 'ADD CONSTRAINT ' + @@ -1114,7 +1099,8 @@ const deleteColumnSql = (column, tableSchema) => { if (column.column_default !== null) { // add column default to down migration - schemaChangesDown.push( + migration.UNSAFE_add( + null, getRunSqlQuery( alterStatement + 'ALTER COLUMN ' + @@ -1127,7 +1113,8 @@ const deleteColumnSql = (column, tableSchema) => { // COMMENT ON COLUMN my_table.my_column IS 'Employee ID number'; if (comment) { - schemaChangesDown.push( + migration.UNSAFE_add( + null, getRunSqlQuery( 'COMMENT ON COLUMN ' + '"' + @@ -1174,8 +1161,8 @@ const deleteColumnSql = (column, tableSchema) => { makeMigrationCall( dispatch, getState, - schemaChangesUp, - schemaChangesDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -1253,16 +1240,11 @@ const addColSql = ( runSqlQueryUp += colDependentSQL.upSql; } - const schemaChangesUp = []; - + const migration = new Migration(); if (colType === 'uuid' && colDefault !== '') { - schemaChangesUp.push( - getRunSqlQuery('CREATE EXTENSION IF NOT EXISTS pgcrypto;') - ); + migration.add(getRunSqlQuery('CREATE EXTENSION IF NOT EXISTS pgcrypto;')); } - schemaChangesUp.push(getRunSqlQuery(runSqlQueryUp)); - let runSqlQueryDown = ''; if (colDependentSQL) { @@ -1284,7 +1266,10 @@ const addColSql = ( colName + '";'; - const schemaChangesDown = [getRunSqlQuery(runSqlQueryDown)]; + migration.add( + getRunSqlQuery(runSqlQueryUp), + getRunSqlQuery(runSqlQueryDown) + ); // Apply migrations const migrationName = @@ -1307,8 +1292,8 @@ const addColSql = ( makeMigrationCall( dispatch, getState, - schemaChangesUp, - schemaChangesDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -1344,10 +1329,9 @@ const deleteConstraintSql = (tableName, cName) => { '"' + cName + '"'; - const schemaChangesUp = [getRunSqlQuery(dropContraintQuery)]; - // pending - const schemaChangesDown = []; + const migration = new Migration(); + migration.add(getRunSqlQuery(dropContraintQuery)); // Apply migrations const migrationName = @@ -1363,8 +1347,8 @@ const deleteConstraintSql = (tableName, cName) => { makeMigrationCall( dispatch, getState, - schemaChangesUp, - schemaChangesDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -1402,8 +1386,11 @@ const saveTableCommentSql = tableType => { : commentQueryBase + sqlEscapeText(updatedComment); const commentDownQuery = commentQueryBase + 'NULL'; - const schemaChangesUp = [getRunSqlQuery(commentUpQuery)]; - const schemaChangesDown = [getRunSqlQuery(commentDownQuery)]; + const migration = new Migration(); + migration.add( + getRunSqlQuery(commentUpQuery), + getRunSqlQuery(commentDownQuery) + ); // Apply migrations const migrationName = @@ -1441,8 +1428,8 @@ const saveTableCommentSql = tableType => { makeMigrationCall( dispatch, getState, - schemaChangesUp, - schemaChangesDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -1466,416 +1453,27 @@ const isColumnUnique = (tableSchema, colName) => { const saveColumnChangesSql = (colName, column, onSuccess) => { // eslint-disable-line no-unused-vars return (dispatch, getState) => { - const columnEdit = getState().tables.modify.columnEdit[colName]; - - const { tableName } = columnEdit; - const colType = columnEdit.type; - const nullable = columnEdit.isNullable; - const unique = columnEdit.isUnique; - const colDefault = (columnEdit.default || '').trim(); - const comment = (columnEdit.comment || '').trim(); - const newName = columnEdit.name.trim(); - const currentSchema = columnEdit.schemaName; - const customFieldName = (columnEdit.customFieldName || '').trim(); - - const tableDef = generateTableDef(tableName, currentSchema); - const table = findTable(getState().tables.allSchemas, tableDef); + const state = getState(); + const columnEdit = state.tables.modify.columnEdit[colName]; + const allSchemas = state.tables.allSchemas; - // check if column type has changed before making it part of the migration - const originalColType = column.udt_name; // "value" - const originalColDefault = column.column_default || ''; // null or "value" - const originalColComment = column.comment || ''; // null or "value" - const originalColNullable = column.is_nullable; // "YES" or "NO" - const originalColUnique = isColumnUnique(table, colName); - - /* column type up/down migration */ - const columnChangesUpQuery = - 'ALTER TABLE ' + - '"' + - currentSchema + - '"' + - '.' + - '"' + - tableName + - '"' + - ' ALTER COLUMN ' + - '"' + - colName + - '"' + - ' TYPE ' + - colType + - ';'; - const columnChangesDownQuery = - 'ALTER TABLE ' + - '"' + - currentSchema + - '"' + - '.' + - '"' + - tableName + - '"' + - ' ALTER COLUMN ' + - '"' + - colName + - '"' + - ' TYPE ' + - column.data_type + - ';'; - const schemaChangesUp = - originalColType !== colType ? [getRunSqlQuery(columnChangesUpQuery)] : []; - const schemaChangesDown = - originalColType !== colType - ? [getRunSqlQuery(columnChangesDownQuery)] - : []; - - /* column custom field up/down migration*/ - const existingCustomColumnNames = getTableCustomColumnNames(table); - const existingRootFields = getTableCustomRootFields(table); - const newCustomColumnNames = { ...existingCustomColumnNames }; - let isCustomFieldNameChanged = false; - if (customFieldName) { - if (customFieldName !== existingCustomColumnNames[colName]) { - isCustomFieldNameChanged = true; - newCustomColumnNames[colName] = customFieldName.trim(); - } - } else { - if (existingCustomColumnNames[colName]) { - isCustomFieldNameChanged = true; - delete newCustomColumnNames[colName]; - } - } - if (isCustomFieldNameChanged) { - schemaChangesUp.push( - getSetCustomRootFieldsQuery( - tableDef, - existingRootFields, - newCustomColumnNames - ) - ); - schemaChangesDown.push( - getSetCustomRootFieldsQuery( - tableDef, - existingRootFields, - existingCustomColumnNames - ) - ); - } - - const colDefaultWithQuotes = - isColTypeString(colType) && !isPostgresFunction(colDefault) - ? `'${colDefault}'` - : colDefault; - const originalColDefaultWithQuotes = - isColTypeString(colType) && !isPostgresFunction(originalColDefault) - ? `'${originalColDefault}'` - : originalColDefault; - - /* column default up/down migration */ - let columnDefaultUpQuery; - let columnDefaultDownQuery; - if (colDefault !== '') { - // ALTER TABLE ALTER COLUMN SET DEFAULT ; - columnDefaultUpQuery = - 'ALTER TABLE ' + - '"' + - currentSchema + - '"' + - '.' + - '"' + - tableName + - '"' + - ' ALTER COLUMN ' + - '"' + - colName + - '"' + - ' SET DEFAULT ' + - colDefaultWithQuotes + - ';'; - } else { - // ALTER TABLE
ALTER COLUMN DROP DEFAULT; - columnDefaultUpQuery = - 'ALTER TABLE ' + - '"' + - currentSchema + - '"' + - '.' + - '"' + - tableName + - '"' + - ' ALTER COLUMN ' + - '"' + - colName + - '"' + - ' DROP DEFAULT;'; - } - - if (originalColDefault !== '') { - columnDefaultDownQuery = - 'ALTER TABLE ' + - '"' + - currentSchema + - '"' + - '.' + - '"' + - tableName + - '"' + - ' ALTER COLUMN ' + - '"' + - colName + - '"' + - ' SET DEFAULT ' + - originalColDefaultWithQuotes + - ';'; - } else { - // there was no default value originally. so drop default. - columnDefaultDownQuery = - 'ALTER TABLE ' + - '"' + - currentSchema + - '"' + - '.' + - '"' + - tableName + - '"' + - ' ALTER COLUMN ' + - '"' + - colName + - '"' + - ' DROP DEFAULT;'; - } - - // check if default is unchanged and then do a drop. if not skip - if (originalColDefault !== colDefault) { - schemaChangesUp.push(getRunSqlQuery(columnDefaultUpQuery)); - schemaChangesDown.push(getRunSqlQuery(columnDefaultDownQuery)); - } - - /* column nullable up/down migration */ - if (nullable) { - // ALTER TABLE
ALTER COLUMN DROP NOT NULL; - const nullableUpQuery = - 'ALTER TABLE ' + - '"' + - currentSchema + - '"' + - '.' + - '"' + - tableName + - '"' + - ' ALTER COLUMN ' + - '"' + - colName + - '"' + - ' DROP NOT NULL;'; - const nullableDownQuery = - 'ALTER TABLE ' + - '"' + - currentSchema + - '"' + - '.' + - '"' + - tableName + - '"' + - ' ALTER COLUMN ' + - '"' + - colName + - '"' + - ' SET NOT NULL;'; - // check with original null - if (originalColNullable !== 'YES') { - schemaChangesUp.push(getRunSqlQuery(nullableUpQuery)); - schemaChangesDown.push(getRunSqlQuery(nullableDownQuery)); - } - } else { - // ALTER TABLE
ALTER COLUMN SET NOT NULL; - const nullableUpQuery = - 'ALTER TABLE ' + - '"' + - currentSchema + - '"' + - '.' + - '"' + - tableName + - '"' + - ' ALTER COLUMN ' + - '"' + - colName + - '"' + - ' SET NOT NULL;'; - const nullableDownQuery = - 'ALTER TABLE ' + - '"' + - currentSchema + - '"' + - '.' + - '"' + - tableName + - '"' + - ' ALTER COLUMN ' + - '"' + - colName + - '"' + - ' DROP NOT NULL;'; - // check with original null - if (originalColNullable !== 'NO') { - schemaChangesUp.push(getRunSqlQuery(nullableUpQuery)); - schemaChangesDown.push(getRunSqlQuery(nullableDownQuery)); - } - } - - /* column unique up/down migration */ - if (unique) { - const uniqueUpQuery = - 'ALTER TABLE ' + - '"' + - currentSchema + - '"' + - '.' + - '"' + - tableName + - '"' + - ' ADD CONSTRAINT ' + - '"' + - tableName + - '_' + - colName + - '_key"' + - ' UNIQUE ' + - '("' + - colName + - '")'; - const uniqueDownQuery = - 'ALTER TABLE ' + - '"' + - currentSchema + - '"' + - '.' + - '"' + - tableName + - '"' + - ' DROP CONSTRAINT ' + - '"' + - tableName + - '_' + - colName + - '_key"'; - // check with original unique - if (!originalColUnique) { - schemaChangesUp.push(getRunSqlQuery(uniqueUpQuery)); - schemaChangesDown.push(getRunSqlQuery(uniqueDownQuery)); - } - } else { - const uniqueDownQuery = - 'ALTER TABLE ' + - '"' + - currentSchema + - '"' + - '.' + - '"' + - tableName + - '"' + - ' ADD CONSTRAINT ' + - '"' + - tableName + - '_' + - colName + - '_key"' + - ' UNIQUE ' + - '("' + - colName + - '")'; - const uniqueUpQuery = - 'ALTER TABLE ' + - '"' + - currentSchema + - '"' + - '.' + - '"' + - tableName + - '"' + - ' DROP CONSTRAINT ' + - '"' + - tableName + - '_' + - colName + - '_key"'; - // check with original unique - if (originalColUnique) { - schemaChangesUp.push(getRunSqlQuery(uniqueUpQuery)); - schemaChangesDown.push(getRunSqlQuery(uniqueDownQuery)); - } - } - - /* column comment up/down migration */ - const columnCommentUpQuery = - 'COMMENT ON COLUMN ' + - '"' + - currentSchema + - '"' + - '.' + - '"' + - tableName + - '"' + - '.' + - '"' + - colName + - '"' + - ' IS ' + - sqlEscapeText(comment); - - const columnCommentDownQuery = - 'COMMENT ON COLUMN ' + - '"' + - currentSchema + - '"' + - '.' + - '"' + - tableName + - '"' + - '.' + - '"' + - colName + - '"' + - ' IS ' + - sqlEscapeText(originalColComment); - - // check if comment is unchanged and then do an update. if not skip - if (originalColComment !== comment) { - schemaChangesUp.push(getRunSqlQuery(columnCommentUpQuery)); - schemaChangesDown.push(getRunSqlQuery(columnCommentDownQuery)); - } - - /* rename column */ - if (newName && colName !== newName) { - if (!gqlPattern.test(newName)) { - return dispatch( - showErrorNotification( - gqlColumnErrorNotif[3], - gqlColumnErrorNotif[1], - gqlColumnErrorNotif[2] - ) - ); - } - schemaChangesUp.push( - getRunSqlQuery( - `alter table "${currentSchema}"."${tableName}" rename column "${colName}" to "${newName}";` - ) - ); - schemaChangesDown.push( - getRunSqlQuery( - `alter table "${currentSchema}"."${tableName}" rename column "${newName}" to "${colName}";` + const onInvalidGqlColName = () => + dispatch( + showErrorNotification( + gqlColumnErrorNotif[3], + gqlColumnErrorNotif[1], + gqlColumnErrorNotif[2] ) ); - } + const { migrationName, migration } = getColumnUpdateMigration( + column, + columnEdit, + allSchemas, + colName, + onInvalidGqlColName + ); // Apply migrations - const migrationName = - 'alter_table_' + - currentSchema + - '_' + - tableName + - '_alter_column_' + - colName; const requestMsg = 'Saving Column Changes...'; const successMsg = 'Column modified'; @@ -1887,12 +1485,12 @@ const saveColumnChangesSql = (colName, column, onSuccess) => { }; const customOnError = () => {}; - if (schemaChangesUp.length > 0) { + if (migration.hasValue()) { makeMigrationCall( dispatch, getState, - schemaChangesUp, - schemaChangesDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -1950,15 +1548,13 @@ const removeUniqueKey = (index, tableName, existingConstraints, callback) => { const { currentSchema } = getState().tables; const existingConstraint = existingConstraints[index]; - // Up migration: Drop the constraint - const sqlUp = [ + const migration = new Migration(); + migration.add( + // Up migration: Drop the constraint getRunSqlQuery( `alter table "${currentSchema}"."${tableName}" drop constraint "${existingConstraint.constraint_name}";` ), - ]; - - // Down Migration: Create the constraint that is being dropped - const sqlDown = [ + // Down Migration: Create the constraint that is being dropped getRunSqlQuery( `alter table "${currentSchema}"."${tableName}" add constraint "${getUniqueConstraintName( tableName, @@ -1966,8 +1562,8 @@ const removeUniqueKey = (index, tableName, existingConstraints, callback) => { )}" unique (${existingConstraint.columns .map(c => `"${c}"`) .join(', ')});` - ), - ]; + ) + ); const migrationName = 'alter_table_' + @@ -2002,8 +1598,8 @@ const removeUniqueKey = (index, tableName, existingConstraints, callback) => { makeMigrationCall( dispatch, getState, - sqlUp, - sqlDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -2030,17 +1626,14 @@ export const toggleTableAsEnum = (isEnum, successCallback, failureCallback) => ( const { currentTable, currentSchema } = getState().tables; const { allSchemas } = getState().tables; - - const upQuery = [ + const migration = new Migration(); + migration.add( getSetTableEnumQuery( generateTableDef(currentTable, currentSchema), !isEnum ), - ]; - const downQuery = [ - getSetTableEnumQuery(generateTableDef(currentTable, currentSchema), isEnum), - ]; - + getSetTableEnumQuery(generateTableDef(currentTable, currentSchema), isEnum) + ); const migrationName = 'alter_table_' + currentSchema + @@ -2090,8 +1683,8 @@ export const toggleTableAsEnum = (isEnum, successCallback, failureCallback) => ( makeMigrationCall( dispatch, getState, - upQuery, - downQuery, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -2122,21 +1715,27 @@ export const saveCheckConstraint = (index, successCb, errorCb) => ( const existingConstraint = allConstraints[index]; - const upQueries = []; - const downQueries = []; - + const migration = new Migration(); if (!isNew) { - upQueries.push( + migration.add( getRunSqlQuery( getDropConstraintSql( existingConstraint.table_name, existingConstraint.table_schema, existingConstraint.constraint_name ) + ), + getRunSqlQuery( + getCreateCheckConstraintSql( + currentTable, + currentSchema, + existingConstraint.constraint_name, + existingConstraint.check + ) ) ); } - upQueries.push( + migration.add( getRunSqlQuery( getCreateCheckConstraintSql( currentTable, @@ -2144,28 +1743,12 @@ export const saveCheckConstraint = (index, successCb, errorCb) => ( newConstraint.name, newConstraint.check ) - ) - ); - - downQueries.push( + ), getRunSqlQuery( getDropConstraintSql(currentTable, currentSchema, newConstraint.name) ) ); - if (!isNew) { - downQueries.push( - getRunSqlQuery( - getCreateCheckConstraintSql( - currentTable, - currentSchema, - existingConstraint.constraint_name, - existingConstraint.check - ) - ) - ); - } - const migrationName = 'alter_table_' + currentSchema + @@ -2194,8 +1777,8 @@ export const saveCheckConstraint = (index, successCb, errorCb) => ( makeMigrationCall( dispatch, getState, - upQueries, - downQueries, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -2221,20 +1804,15 @@ const saveUniqueKey = ( const columns = uniqueKey.map(c => allColumns[c].name); const existingConstraint = existingConstraints[index]; - // Down migration - const downMigration = []; - // drop the newly created constraint - downMigration.push( - getRunSqlQuery( - `alter table "${currentSchema}"."${tableName}" drop constraint "${getUniqueConstraintName( - tableName, - columns - )}";` - ) - ); + // Migration + const migration = new Migration(); // if any constraint is being dropped, create it back if (index < numUniqueKeys - 1) { - downMigration.push( + // drop the old constraint if there is any + migration.add( + getRunSqlQuery( + `alter table "${currentSchema}"."${tableName}" drop constraint "${existingConstraint.constraint_name}";` + ), getRunSqlQuery( `alter table "${currentSchema}"."${tableName}" add constraint "${getUniqueConstraintName( tableName, @@ -2246,24 +1824,20 @@ const saveUniqueKey = ( ); } - // up migration - const upMigration = []; - // drop the old constraint if there is any - if (index < numUniqueKeys - 1) { - upMigration.push( - getRunSqlQuery( - `alter table "${currentSchema}"."${tableName}" drop constraint "${existingConstraint.constraint_name}";` - ) - ); - } - // create the new constraint - upMigration.push( + migration.add( getRunSqlQuery( `alter table "${currentSchema}"."${tableName}" add constraint "${getUniqueConstraintName( tableName, columns )}" unique (${columns.map(c => `"${c}"`).join(', ')});` + ), + // drop the newly created constraint + getRunSqlQuery( + `alter table "${currentSchema}"."${tableName}" drop constraint "${getUniqueConstraintName( + tableName, + columns + )}";` ) ); @@ -2294,8 +1868,8 @@ const saveUniqueKey = ( makeMigrationCall( dispatch, getState, - upMigration, - downMigration, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -2319,15 +1893,19 @@ export const setViewCustomColumnNames = ( const existingCustomRootFields = getTableCustomRootFields(view); const existingColumnNames = getTableCustomColumnNames(view); - const upQuery = getSetCustomRootFieldsQuery( - viewDef, - existingCustomRootFields, - sanitiseColumnNames(customColumnNames) - ); - const downQuery = getSetCustomRootFieldsQuery( - viewDef, - existingCustomRootFields, - existingColumnNames + const migration = new Migration(); + + migration.add( + getSetCustomRootFieldsQuery( + viewDef, + existingCustomRootFields, + sanitiseColumnNames(customColumnNames) + ), + getSetCustomRootFieldsQuery( + viewDef, + existingCustomRootFields, + existingColumnNames + ) ); const migrationName = 'alter_view_custom_column_names'; @@ -2351,8 +1929,8 @@ export const setViewCustomColumnNames = ( makeMigrationCall( dispatch, getState, - [upQuery], - [downQuery], + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, diff --git a/console/src/components/Services/Data/TablePermissions/Actions.js b/console/src/components/Services/Data/TablePermissions/Actions.js index 61e71e7a18959..9083a9e91ed0b 100644 --- a/console/src/components/Services/Data/TablePermissions/Actions.js +++ b/console/src/components/Services/Data/TablePermissions/Actions.js @@ -13,6 +13,7 @@ import { getDropPermissionQuery, } from '../../../Common/utils/v1QueryUtils'; import { capitalize } from '../../../Common/utils/jsUtils'; +import Migration from '../../../../utils/migration/Migration'; export const PERM_OPEN_EDIT = 'ModifyTable/PERM_OPEN_EDIT'; export const PERM_SET_FILTER_TYPE = 'ModifyTable/PERM_SET_FILTER_TYPE'; @@ -261,11 +262,11 @@ const permRemoveRole = (tableSchema, roleName) => { p => p.role_name === role ); - const permissionsUpQueries = []; - const permissionsDownQueries = []; + const migration = new Migration(); if (currRolePermissions && currRolePermissions.permissions) { Object.keys(currRolePermissions.permissions).forEach(type => { + // up const deleteQuery = { type: 'drop_' + type + '_permission', args: { @@ -273,6 +274,7 @@ const permRemoveRole = (tableSchema, roleName) => { role: role, }, }; + // down const createQuery = { type: 'create_' + type + '_permission', args: { @@ -281,8 +283,7 @@ const permRemoveRole = (tableSchema, roleName) => { permission: currRolePermissions.permissions[type], }, }; - permissionsUpQueries.push(deleteQuery); - permissionsDownQueries.push(createQuery); + migration.add(deleteQuery, createQuery); }); } @@ -308,8 +309,8 @@ const permRemoveRole = (tableSchema, roleName) => { makeMigrationCall( dispatch, getState, - permissionsUpQueries, - permissionsDownQueries, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -328,8 +329,7 @@ const permRemoveMultipleRoles = tableSchema => { const table = tableSchema.table_name; const roles = permissionsState.bulkSelect; - const permissionsUpQueries = []; - const permissionsDownQueries = []; + const migration = new Migration(); const currentPermissions = tableSchema.permissions; roles.map(role => { @@ -352,8 +352,7 @@ const permRemoveMultipleRoles = tableSchema => { permission: currentRolePermission[0].permissions[type], }, }; - permissionsUpQueries.push(deleteQuery); - permissionsDownQueries.push(createQuery); + migration.add(deleteQuery, createQuery); }); }); @@ -379,8 +378,8 @@ const permRemoveMultipleRoles = tableSchema => { makeMigrationCall( dispatch, getState, - permissionsUpQueries, - permissionsDownQueries, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -393,8 +392,7 @@ const permRemoveMultipleRoles = tableSchema => { const applySamePermissionsBulk = (tableSchema, arePermissionsModified) => { return (dispatch, getState) => { - const permissionsUpQueries = []; - const permissionsDownQueries = []; + const migration = new Migration(); const allSchemas = getState().tables.allSchemas; const currentSchema = getState().tables.currentSchema; @@ -450,8 +448,7 @@ const applySamePermissionsBulk = (tableSchema, arePermissionsModified) => { }, }; - permissionsUpQueries.push(deleteQuery); - permissionsDownQueries.unshift(createQuery); + migration.add(deleteQuery, createQuery); } // modify query depending on table and action @@ -483,8 +480,7 @@ const applySamePermissionsBulk = (tableSchema, arePermissionsModified) => { role: applyTo.role, }, }; - permissionsUpQueries.push(createQuery); - permissionsDownQueries.unshift(deleteQuery); + migration.add(createQuery, deleteQuery); }); // Apply migration @@ -510,8 +506,8 @@ const applySamePermissionsBulk = (tableSchema, arePermissionsModified) => { makeMigrationCall( dispatch, getState, - permissionsUpQueries, - permissionsDownQueries, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -534,8 +530,7 @@ const copyRolePermissions = ( onSuccess ) => { return (dispatch, getState) => { - const permissionsUpQueries = []; - const permissionsDownQueries = []; + const migration = new Migration(); const allSchemas = getState().tables.allSchemas; const currentSchema = getState().tables.currentSchema; @@ -580,9 +575,7 @@ const copyRolePermissions = ( toRole, currPermissions ); - - permissionsUpQueries.push(deleteQuery); - permissionsDownQueries.push(createQuery); + migration.add(deleteQuery, createQuery); } if (toBeAppliedPermissions) { @@ -598,9 +591,7 @@ const copyRolePermissions = ( tableDef, toRole ); - - permissionsUpQueries.push(createQuery); - permissionsDownQueries.push(deleteQuery); + migration.add(createQuery, deleteQuery); } }); }); @@ -631,8 +622,8 @@ const copyRolePermissions = ( makeMigrationCall( dispatch, getState, - permissionsUpQueries, - permissionsDownQueries, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -733,8 +724,7 @@ const permChangePermissions = changeType => { p => p.role_name === role ); - const permissionsUpQueries = []; - const permissionsDownQueries = []; + const migration = new Migration(); if (query === 'select' && !limitEnabled) { delete permissionsState[query].limit; } @@ -755,8 +745,7 @@ const permChangePermissions = changeType => { permission: prevPermissionsState[query], }, }; - permissionsUpQueries.push(deleteQuery); - permissionsDownQueries.push(createQuery); + migration.add(deleteQuery, createQuery); } if (changeType === permChangeTypes.save) { @@ -776,14 +765,9 @@ const permChangePermissions = changeType => { role: role, }, }; - - permissionsUpQueries.push(createQuery); - permissionsDownQueries.push(deleteQuery); + migration.add(createQuery, deleteQuery); } - // Reverse order of down migration - permissionsDownQueries.reverse(); - // Apply migration const migrationName = changeType + @@ -816,8 +800,8 @@ const permChangePermissions = changeType => { makeMigrationCall( dispatch, getState, - permissionsUpQueries, - permissionsDownQueries, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, diff --git a/console/src/components/Services/Data/TableRelationships/Actions.js b/console/src/components/Services/Data/TableRelationships/Actions.js index 8a1a606c5de6c..983354334e704 100644 --- a/console/src/components/Services/Data/TableRelationships/Actions.js +++ b/console/src/components/Services/Data/TableRelationships/Actions.js @@ -13,6 +13,7 @@ import { getRemoteRelPayload, parseRemoteRelationship, } from './RemoteRelationships/utils'; +import Migration from '../../../../utils/migration/Migration'; export const SET_MANUAL_REL_ADD = 'ModifyTable/SET_MANUAL_REL_ADD'; export const MANUAL_REL_SET_TYPE = 'ModifyTable/MANUAL_REL_SET_TYPE'; @@ -71,18 +72,20 @@ export const saveRemoteRelationship = ( } return dispatch(showErrorNotification(errorMsg, e.message)); } - - const upQuery = [getSaveRemoteRelQuery(remoteRelQueryArgs, !existingRel)]; - const downQuery = []; + const migration = new Migration(); + let downQry; if (isNew) { - downQuery.push(getDropRemoteRelQuery(state.name, state.table)); + downQry = getDropRemoteRelQuery(state.name, state.table); } else { - const downQueryArgs = getSaveRemoteRelQuery( + downQry = getSaveRemoteRelQuery( getRemoteRelPayload(parseRemoteRelationship(existingRel)), isNew ); - downQuery.push(downQueryArgs); } + migration.add( + getSaveRemoteRelQuery(remoteRelQueryArgs, !existingRel), + downQry + ); // Apply migrations const migrationName = `table_${table.name}_${ @@ -111,8 +114,8 @@ export const saveRemoteRelationship = ( makeMigrationCall( dispatch, getState, - upQuery, - downQuery, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -139,18 +142,16 @@ export const dropRemoteRelationship = ( return; } - const downQuery = [ + const table = state.table; + const migration = new Migration(); + + migration.add( + getDropRemoteRelQuery(existingRel.remote_relationship_name, table), getSaveRemoteRelQuery( getRemoteRelPayload(parseRemoteRelationship(existingRel)), true - ), - ]; - - const table = state.table; - const upQuery = [ - getDropRemoteRelQuery(existingRel.remote_relationship_name, table), - ]; - + ) + ); // Apply migrations const migrationName = `table_${table.name}_drop_remote_relationship_${state.name}`; @@ -173,8 +174,8 @@ export const dropRemoteRelationship = ( makeMigrationCall( dispatch, getState, - upQuery, - downQuery, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -217,32 +218,32 @@ const manualRelRSchemaChanged = rSchema => ({ const saveRenameRelationship = (oldName, newName, tableName, callback) => { return (dispatch, getState) => { const currentSchema = getState().tables.currentSchema; - const migrateUp = [ - { - type: 'rename_relationship', - args: { - table: { - name: tableName, - schema: currentSchema, - }, - name: oldName, - new_name: newName, + const migrateUp = { + type: 'rename_relationship', + args: { + table: { + name: tableName, + schema: currentSchema, }, + name: oldName, + new_name: newName, }, - ]; - const migrateDown = [ - { - type: 'rename_relationship', - args: { - table: { - name: tableName, - schema: currentSchema, - }, - name: newName, - new_name: oldName, + }; + + const migrateDown = { + type: 'rename_relationship', + args: { + table: { + name: tableName, + schema: currentSchema, }, + name: newName, + new_name: oldName, }, - ]; + }; + + const migration = new Migration(); + migration.add(migrateUp, migrateDown); // Apply migrations const migrationName = `rename_relationship_${oldName}_to_${newName}_schema_${currentSchema}_table_${tableName}`; @@ -259,8 +260,8 @@ const saveRenameRelationship = (oldName, newName, tableName, callback) => { makeMigrationCall( dispatch, getState, - migrateUp, - migrateDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -374,9 +375,9 @@ const generateRelationshipsQuery = relMeta => { const deleteRelMigrate = relMeta => (dispatch, getState) => { const { upQuery, downQuery } = generateRelationshipsQuery(relMeta); - const relChangesUp = [downQuery]; - const relChangesDown = [upQuery]; + const migration = new Migration(); + migration.add(downQuery, upQuery); // upquery from generateRelationshipsQuery used as downMigratio and vice versa // Apply migrations const migrationName = `drop_relationship_${relMeta.relName}_${relMeta.lSchema}_table_${relMeta.lTable}`; @@ -393,8 +394,8 @@ const deleteRelMigrate = relMeta => (dispatch, getState) => { makeMigrationCall( dispatch, getState, - relChangesUp, - relChangesDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -418,8 +419,9 @@ const addRelNewFromStateMigrate = () => (dispatch, getState) => { rSchema: state.rSchema, isUnique: state.isUnique, }); - const relChangesUp = [upQuery]; - const relChangesDown = [downQuery]; + const migration = new Migration(); + + migration.add(upQuery, downQuery); // Apply migrations const migrationName = `add_relationship_${state.name}_table_${state.lSchema}_${state.lTable}`; @@ -448,8 +450,8 @@ const addRelNewFromStateMigrate = () => (dispatch, getState) => { makeMigrationCall( dispatch, getState, - relChangesUp, - relChangesDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -492,32 +494,29 @@ const addRelViewMigrate = (tableSchema, toggleEditor) => ( columnMapping[colMap.column] = colMap.refColumn; }); - const relChangesUp = [ - { - type: isObjRel - ? 'create_object_relationship' - : 'create_array_relationship', - args: { - name: relName, - table: { name: currentTableName, schema: currentTableSchema }, - using: { - manual_configuration: { - remote_table: { name: rTable, schema: rSchema }, - column_mapping: columnMapping, - }, + //Migration + const migration = new Migration(); + const relChangesUp = { + type: isObjRel ? 'create_object_relationship' : 'create_array_relationship', + args: { + name: relName, + table: { name: currentTableName, schema: currentTableSchema }, + using: { + manual_configuration: { + remote_table: { name: rTable, schema: rSchema }, + column_mapping: columnMapping, }, }, }, - ]; - const relChangesDown = [ - { - type: 'drop_relationship', - args: { - table: { name: currentTableName, schema: currentTableSchema }, - relationship: relName, - }, + }; + const relChangesDown = { + type: 'drop_relationship', + args: { + table: { name: currentTableName, schema: currentTableSchema }, + relationship: relName, }, - ]; + }; + migration.add(relChangesUp, relChangesDown); // Apply migrations const migrationName = `create_relationship_${relName}_${currentTableSchema}_table_${currentTableName}`; @@ -562,8 +561,8 @@ const addRelViewMigrate = (tableSchema, toggleEditor) => ( makeMigrationCall( dispatch, getState, - relChangesUp, - relChangesDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -702,8 +701,11 @@ const getAllUnTrackedRelations = (allSchemas, currentSchema) => { }; const autoTrackRelations = autoTrackData => (dispatch, getState) => { - const relChangesUp = autoTrackData.map(data => data.upQuery); - const relChangesDown = autoTrackData.map(data => data.downQuery); + const migration = new Migration(); + autoTrackData.forEach(({ upQuery, downQuery }) => + migration.add(upQuery, downQuery) + ); + // Apply migrations const migrationName = 'track_all_relationships'; @@ -718,8 +720,8 @@ const autoTrackRelations = autoTrackData => (dispatch, getState) => { makeMigrationCall( dispatch, getState, - relChangesUp, - relChangesDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -734,8 +736,8 @@ const autoAddRelName = obj => (dispatch, getState) => { const currentSchema = getState().tables.currentSchema; const relName = obj.upQuery.args.name; - const relChangesUp = [obj.upQuery]; - const relChangesDown = [obj.downQuery]; + const migration = new Migration(); + migration.add(obj.upQuery, obj.downQuery); // Apply migrations const migrationName = `add_relationship_${relName}_table_${currentSchema}_${obj.data.tableName}`; @@ -752,8 +754,8 @@ const autoAddRelName = obj => (dispatch, getState) => { makeMigrationCall( dispatch, getState, - relChangesUp, - relChangesDown, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, diff --git a/console/src/components/Services/Data/utils.js b/console/src/components/Services/Data/utils.js index c324e45d66eb2..ceb6e7a4a515d 100644 --- a/console/src/components/Services/Data/utils.js +++ b/console/src/components/Services/Data/utils.js @@ -776,6 +776,8 @@ const postgresFunctionTester = /.*\(\)$/gm; export const isPostgresFunction = str => new RegExp(postgresFunctionTester).test(str); +export const isTypeCast = (str = '') => str.split('::').length > 1; + export const getEstimateCountQuery = (schemaName, tableName) => { return ` SELECT diff --git a/console/src/components/Services/Events/ServerIO.ts b/console/src/components/Services/Events/ServerIO.ts index 39c5824fa1a1b..fd2443e6e1873 100644 --- a/console/src/components/Services/Events/ServerIO.ts +++ b/console/src/components/Services/Events/ServerIO.ts @@ -61,6 +61,7 @@ import { } from '../Common/Notification'; import { EventTriggerProperty } from './EventTriggers/Modify/utils'; import { getLogsTableDef } from './utils'; +import Migration from '../../../utils/migration/Migration'; export const fetchTriggers = ( kind: Nullable @@ -122,9 +123,11 @@ export const addScheduledTrigger = ( } return dispatch(showErrorNotification(errorMsg, validationError)); } - - const upQuery = generateCreateScheduledTriggerQuery(state); - const downQuery = getDropScheduledTriggerQuery(state.name); + const migration = new Migration(); + migration.add( + generateCreateScheduledTriggerQuery(state), + getDropScheduledTriggerQuery(state.name) + ); const migrationName = `create_scheduled_trigger_${state.name}`; const requestMsg = 'Creating scheduled trigger...'; @@ -153,8 +156,8 @@ export const addScheduledTrigger = ( return makeMigrationCall( dispatch, getState, - [upQuery], - [downQuery], + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -195,22 +198,28 @@ export const saveScheduledTrigger = ( return null; } } - - const replaceQueryUp = generateUpdateScheduledTriggerQuery(state); - const replaceQueryDown = generateUpdateScheduledTriggerQuery( - parseServerScheduledTrigger(existingTrigger) - ); - - const upRenameQueries = [ - generateCreateScheduledTriggerQuery(state), - getDropScheduledTriggerQuery(existingTrigger.name), - ]; - const downRenameQueries = [ - getDropScheduledTriggerQuery(state.name), - generateCreateScheduledTriggerQuery( - parseServerScheduledTrigger(existingTrigger) - ), - ]; + const migration = new Migration(); + if (!isRenamed) { + migration.add( + generateUpdateScheduledTriggerQuery(state), + generateUpdateScheduledTriggerQuery( + parseServerScheduledTrigger(existingTrigger) + ) + ); + } else { + // drop existing + migration.add( + getDropScheduledTriggerQuery(existingTrigger.name), + generateCreateScheduledTriggerQuery( + parseServerScheduledTrigger(existingTrigger) + ) + ); + // create new + migration.add( + generateCreateScheduledTriggerQuery(state), + getDropScheduledTriggerQuery(state.name) + ); + } const migrationName = `update_scheduled_trigger_${existingTrigger.name}_to_${state.name}`; const requestMsg = 'Updating scheduled trigger...'; @@ -246,8 +255,8 @@ export const saveScheduledTrigger = ( return makeMigrationCall( dispatch, getState, - isRenamed ? upRenameQueries : [replaceQueryUp], - isRenamed ? downRenameQueries : [replaceQueryDown], + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -274,10 +283,10 @@ export const deleteScheduledTrigger = ( } return; } - - const upQuery = getDropScheduledTriggerQuery(trigger.name); - const downQuery = generateCreateScheduledTriggerQuery( - parseServerScheduledTrigger(trigger) + const migration = new Migration(); + migration.add( + getDropScheduledTriggerQuery(trigger.name), + generateCreateScheduledTriggerQuery(parseServerScheduledTrigger(trigger)) ); const migrationName = `delete_scheduled_trigger_${trigger.name}`; @@ -301,8 +310,8 @@ export const deleteScheduledTrigger = ( makeMigrationCall( dispatch, getState, - [upQuery], - [downQuery], + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -328,8 +337,11 @@ export const createEventTrigger = ( const migrationName = `create_event_trigger_${state.name.trim()}`; - const upQuery = generateCreateEventTriggerQuery(state); - const downQuery = getDropEventTriggerQuery(state.name); + const migration = new Migration(); + migration.add( + generateCreateEventTriggerQuery(state), + getDropEventTriggerQuery(state.name) + ); const requestMsg = 'Creating event trigger...'; const successMsg = 'Event Trigger Created'; @@ -352,8 +364,8 @@ export const createEventTrigger = ( makeMigrationCall( dispatch, getState, - [upQuery], - [downQuery], + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -427,9 +439,10 @@ export const modifyEventTrigger = ( default: break; } + const migration = new Migration(); + migration.add(upQuery, downQuery); const migrationName = `set_et_${state.name.trim()}_${property}`; - const requestMsg = 'Saving...'; const successMsg = 'Saved'; @@ -449,8 +462,8 @@ export const modifyEventTrigger = ( return makeMigrationCall( dispatch, getState, - [upQuery], - [downQuery], + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -474,10 +487,10 @@ export const deleteEventTrigger = ( if (!isOk) { return undefined; } - - const upQuery = getDropEventTriggerQuery(trigger.name); - const downQuery = generateCreateEventTriggerQuery( - parseServerETDefinition(trigger) + const migration = new Migration(); + migration.add( + getDropEventTriggerQuery(trigger.name), + generateCreateEventTriggerQuery(parseServerETDefinition(trigger)) ); const migrationName = `delete_et_${trigger.name}`; @@ -503,8 +516,8 @@ export const deleteEventTrigger = ( return makeMigrationCall( dispatch, getState, - [upQuery], - [downQuery], + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, diff --git a/console/src/components/Services/RemoteSchema/Actions.js b/console/src/components/Services/RemoteSchema/Actions.js index 57b8f28603777..30e85348b6183 100644 --- a/console/src/components/Services/RemoteSchema/Actions.js +++ b/console/src/components/Services/RemoteSchema/Actions.js @@ -166,7 +166,6 @@ const makeRequest = ( if (globals.consoleMode === CLI_CONSOLE_MODE) { dispatch(loadMigrationStatus()); // don't call for server mode } - // dispatch(loadTriggers()); if (successMsg) { dispatch(showSuccessNotification(successMsg)); } diff --git a/console/src/components/Services/RemoteSchema/Add/addRemoteSchemaReducer.js b/console/src/components/Services/RemoteSchema/Add/addRemoteSchemaReducer.js index 39bc6b472bd32..3cd3c9ce314c8 100644 --- a/console/src/components/Services/RemoteSchema/Add/addRemoteSchemaReducer.js +++ b/console/src/components/Services/RemoteSchema/Add/addRemoteSchemaReducer.js @@ -15,6 +15,7 @@ import { appPrefix } from '../constants'; import globals from '../../../../Globals'; import { clearIntrospectionSchemaCache } from '../graphqlUtils'; +import Migration from '../../../../utils/migration/Migration'; const prefixUrl = globals.urlPrefix + appPrefix; @@ -187,19 +188,8 @@ const addRemoteSchema = () => { }, }; - const upQueryArgs = []; - upQueryArgs.push(payload); - const downQueryArgs = []; - downQueryArgs.push(downPayload); - const upQuery = { - type: 'bulk', - args: upQueryArgs, - }; - - const downQuery = { - type: 'bulk', - args: downQueryArgs, - }; + const migration = new Migration(); + migration.add(payload, downPayload); const requestMsg = 'Adding remote schema...'; const successMsg = 'Remote schema added successfully'; @@ -223,8 +213,8 @@ const addRemoteSchema = () => { dispatch({ type: ADDING_REMOTE_SCHEMA }); return dispatch( makeRequest( - upQuery.args, - downQuery.args, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -269,18 +259,9 @@ const deleteRemoteSchema = () => { ...currState.editState.originalHeaders, ]; - const upQueryArgs = []; - upQueryArgs.push(payload); - const downQueryArgs = []; - downQueryArgs.push(downPayload); - const upQuery = { - type: 'bulk', - args: upQueryArgs, - }; - const downQuery = { - type: 'bulk', - args: downQueryArgs, - }; + const migration = new Migration(); + migration.add(payload, downPayload); + const requestMsg = 'Deleting remote schema...'; const successMsg = 'Remote schema deleted successfully'; const errorMsg = 'Delete remote schema failed'; @@ -301,8 +282,8 @@ const deleteRemoteSchema = () => { dispatch({ type: DELETING_REMOTE_SCHEMA }); return dispatch( makeRequest( - upQuery.args, - downQuery.args, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, @@ -318,8 +299,8 @@ const modifyRemoteSchema = () => { return (dispatch, getState) => { const currState = getState().remoteSchemas.addData; const remoteSchemaName = currState.name.trim().replace(/ +/g, ''); - const upQueryArgs = []; - const downQueryArgs = []; + // const url = Endpoints.getSchema; + const migration = new Migration(); const migrationName = 'update_remote_schema_' + remoteSchemaName; const deleteRemoteSchemaUp = { type: 'remove_remote_schema', @@ -360,8 +341,6 @@ const modifyRemoteSchema = () => { ...resolveObj, }, }; - upQueryArgs.push(deleteRemoteSchemaUp); - upQueryArgs.push(createRemoteSchemaUp); // Delete the new one and create the old one const deleteRemoteSchemaDown = { @@ -398,20 +377,12 @@ const modifyRemoteSchema = () => { ...resolveDownObj, }, }; - - downQueryArgs.push(deleteRemoteSchemaDown); - downQueryArgs.push(createRemoteSchemaDown); + // old schema + migration.add(deleteRemoteSchemaUp, createRemoteSchemaDown); + // new schema + migration.add(createRemoteSchemaUp, deleteRemoteSchemaDown); // End of down - const upQuery = { - type: 'bulk', - args: upQueryArgs, - }; - const downQuery = { - type: 'bulk', - args: downQueryArgs, - }; - const requestMsg = 'Modifying remote schema...'; const successMsg = 'Remote schema modified'; const errorMsg = 'Modify remote schema failed'; @@ -433,8 +404,8 @@ const modifyRemoteSchema = () => { dispatch({ type: MODIFYING_REMOTE_SCHEMA }); return dispatch( makeRequest( - upQuery.args, - downQuery.args, + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, diff --git a/console/src/components/Services/Settings/Actions.js b/console/src/components/Services/Settings/Actions.js index 7628961a218d0..c8f859d125a41 100644 --- a/console/src/components/Services/Settings/Actions.js +++ b/console/src/components/Services/Settings/Actions.js @@ -25,6 +25,7 @@ import { generateReplaceMetadataQuery, resetMetadataQuery, } from '../../Common/utils/v1QueryUtils'; +import Migration from '../../../utils/migration/Migration'; const LOAD_INCONSISTENT_OBJECTS = 'Metadata/LOAD_INCONSISTENT_OBJECTS'; const LOADING_METADATA = 'Metadata/LOADING_METADATA'; @@ -85,8 +86,11 @@ export const replaceMetadata = (newMetadata, successCb, errorCb) => ( getState ) => { const exportSuccessCb = oldMetadata => { - const upQuery = generateReplaceMetadataQuery(newMetadata); - const downQuery = generateReplaceMetadataQuery(oldMetadata); + const migration = new Migration(); + migration.add( + generateReplaceMetadataQuery(newMetadata), + generateReplaceMetadataQuery(oldMetadata) + ); const migrationName = 'replace_metadata'; @@ -104,8 +108,8 @@ export const replaceMetadata = (newMetadata, successCb, errorCb) => ( makeMigrationCall( dispatch, getState, - [upQuery], - [downQuery], + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, diff --git a/console/src/components/Services/Types/ServerIO.js b/console/src/components/Services/Types/ServerIO.js index 263b3b9648e95..d95f80d6d1c1b 100644 --- a/console/src/components/Services/Types/ServerIO.js +++ b/console/src/components/Services/Types/ServerIO.js @@ -17,6 +17,7 @@ import { generateSetCustomTypesQuery, } from '../../Common/utils/v1QueryUtils'; import { getConfirmation } from '../../Common/utils/jsUtils'; +import Migration from '../../../utils/migration/Migration'; export const setCustomTypes = (dispatch, response) => { dispatch({ @@ -63,9 +64,10 @@ export const setCustomGraphQLTypes = (types, successCb, errorCb) => ( const hydratedTypes = hydrateTypeRelationships(types, existingTypes); - const upQuery = generateSetCustomTypesQuery(reformCustomTypes(hydratedTypes)); - const downQuery = generateSetCustomTypesQuery( - reformCustomTypes(existingTypes) + const migration = new Migration(); + migration.add( + generateSetCustomTypesQuery(reformCustomTypes(hydratedTypes)), + generateSetCustomTypesQuery(reformCustomTypes(existingTypes)) ); const migrationName = 'set_custom_types'; @@ -89,8 +91,8 @@ export const setCustomGraphQLTypes = (types, successCb, errorCb) => ( makeMigrationCall( dispatch, getState, - [upQuery], - [downQuery], + migration.upMigration, + migration.downMigration, migrationName, customOnSuccess, customOnError, diff --git a/console/src/types.ts b/console/src/types.ts index 9dbe5eea58fd1..f10de51c361ec 100644 --- a/console/src/types.ts +++ b/console/src/types.ts @@ -65,3 +65,14 @@ export type ReduxStore = Store; // Router Utils export type ReplaceRouterState = (route: string) => void; + +// HGE common types +export type RunSqlType = { + type: string; + version?: number; + args: { + cascade?: boolean; + read_only?: boolean; + sql: string; + }; +}; diff --git a/console/src/utils/migration/Migration.ts b/console/src/utils/migration/Migration.ts new file mode 100644 index 0000000000000..8a69a9d04aabf --- /dev/null +++ b/console/src/utils/migration/Migration.ts @@ -0,0 +1,40 @@ +import { getDownQueryComments } from './utils'; +import { RunSqlType } from '../../types'; + +// TODO use well typed interface after generating +export interface RunSQLQueryType { + type: string; + args: unknown; +} + +export default class Migration { + upMigration: RunSQLQueryType[]; + downMigration: RunSQLQueryType[]; + constructor() { + this.upMigration = []; + this.downMigration = []; + } + + hasValue = () => this.upMigration.length > 0; + + // if there is no down migration possible, call the function with argument empty or null + add = (up: RunSQLQueryType, down: RunSQLQueryType) => { + this.upMigration = [...this.upMigration, up]; + if (down) this.downMigration = [down, ...this.downMigration]; + // auto generate down query comments based on up queries + else if (up.type === 'run_sql') + // ensures the pg comments are generated only for run_sql up migrations + this.downMigration = [ + getDownQueryComments([up as RunSqlType])[0], // reusing the method which works with array + ...this.downMigration, + ]; + }; + + // this is called when there is only one migration for at that particular index + UNSAFE_add = (up?: RunSQLQueryType, down?: RunSQLQueryType) => { + if (up !== undefined && up !== null) + this.upMigration = [...this.upMigration, up]; + if (down !== undefined && down !== null) + this.downMigration = [down, ...this.downMigration]; + }; +} diff --git a/console/src/utils/migration/utils.ts b/console/src/utils/migration/utils.ts new file mode 100644 index 0000000000000..b8695d4bccced --- /dev/null +++ b/console/src/utils/migration/utils.ts @@ -0,0 +1,291 @@ +import { + generateTableDef, + findTable, + getTableCustomColumnNames, + getTableCustomRootFields, + Table, +} from '../../components/Common/utils/pgUtils'; +import { isColumnUnique } from '../../components/Services/Data/TableModify/ModifyActions'; +import { + getRunSqlQuery, + getSetCustomRootFieldsQuery, + CustomRootFields, +} from '../../components/Common/utils/v1QueryUtils'; +import { + isColTypeString, + isPostgresFunction, + isTypeCast, +} from '../../components/Services/Data/utils'; +import gqlPattern from '../../components/Services/Data/Common/GraphQLValidation'; +import { sqlEscapeText } from '../../components/Common/utils/sqlUtils'; +import Migration from './Migration'; +import { RunSqlType } from '../../types'; + +// Types +export interface NewColumnType { + tableName: string; + type: string; + isNullable: boolean; + isUnique: boolean; + default?: string; + comment?: string; + name: string; + schemaName?: string; + customFieldName?: string; +} + +export interface SchemaType { + table_name: string; + table_type: string; + table_schema: string; +} + +export interface OldColumnType { + udt_name: string; + data_type: string; + column_default: string | null; + comment: string | null; + is_nullable?: string; +} + +// utils + +const parseNewCol = (newColumn: NewColumnType) => ({ + tableName: newColumn.tableName, + colType: newColumn.type, + nullable: newColumn.isNullable, + unique: newColumn.isUnique, + colDefault: (newColumn.default || '').trim(), + comment: (newColumn.comment || '').trim(), + newName: newColumn.name.trim(), + currentSchema: newColumn.schemaName, + customFieldName: (newColumn.customFieldName || '').trim(), +}); + +const parseOldColumns = (oldColumn: OldColumnType) => ({ + originalColType: oldColumn.udt_name, + originalData_type: oldColumn.data_type, + originalColDefault: oldColumn.column_default || '', + originalColComment: oldColumn.comment || '', + originalColNullable: oldColumn.is_nullable, +}); + +// utility to compare old & new columns and generate up &down migrations +export const getColumnUpdateMigration = ( + oldColumn: OldColumnType, + newColumn: NewColumnType, + allSchemas: Table[], + colName: string, + onInvalidGqlColName: () => void +) => { + const { + tableName, + colType, + nullable, + unique, + colDefault, + comment, + newName, + currentSchema, + customFieldName, + } = parseNewCol(newColumn); + + const tableDef = generateTableDef(tableName, currentSchema); + const table = findTable(allSchemas, tableDef) as Table; + + const { + originalColType, + originalData_type, + originalColDefault, + originalColComment, + originalColNullable, + } = parseOldColumns(oldColumn); + + const originalColUnique = isColumnUnique(table, colName); + + const ALTER_TABLE = `ALTER TABLE "${currentSchema}"."${tableName}"`; + const ALTER_COL = `${ALTER_TABLE} ALTER COLUMN "${colName}"`; + + /* column type up/down migration */ + const columnChangesUpQuery = `${ALTER_COL} TYPE ${colType};`; + const columnChangesDownQuery = `${ALTER_COL} TYPE ${originalData_type};`; + + // instantiate MIgration Helper + const migration = new Migration(); + + if (originalColType !== colType) { + migration.add( + getRunSqlQuery(columnChangesUpQuery), + getRunSqlQuery(columnChangesDownQuery) + ); + } + + /* column custom field up/down migration */ + const existingCustomColumnNames = getTableCustomColumnNames(table); + const existingRootFields = getTableCustomRootFields( + table + ) as CustomRootFields; + const newCustomColumnNames = { ...existingCustomColumnNames }; + let isCustomFieldNameChanged = false; + if (customFieldName) { + if (customFieldName !== existingCustomColumnNames[colName]) { + isCustomFieldNameChanged = true; + newCustomColumnNames[colName] = customFieldName.trim(); + } + } else if (existingCustomColumnNames[colName]) { + isCustomFieldNameChanged = true; + delete newCustomColumnNames[colName]; + } + if (isCustomFieldNameChanged) { + migration.add( + getSetCustomRootFieldsQuery( + tableDef, + existingRootFields, + newCustomColumnNames + ), + getSetCustomRootFieldsQuery( + tableDef, + existingRootFields, + existingCustomColumnNames + ) + ); + } + + const colDefaultWithQuotes = + isColTypeString(colType) && + !isPostgresFunction(colDefault) && + !isTypeCast(colDefault) + ? `'${colDefault}'` + : colDefault; + const originalColDefaultWithQuotes = + isColTypeString(colType) && + !isPostgresFunction(originalColDefault) && + !isTypeCast(originalColDefault) + ? `'${originalColDefault}'` + : originalColDefault; + + /* column default up/down migration */ + let columnDefaultUpQuery; + let columnDefaultDownQuery; + if (colDefault !== '') { + // ALTER TABLE ONLY
ALTER COLUMN SET DEFAULT ; + columnDefaultUpQuery = `ALTER TABLE ONLY "${currentSchema}"."${tableName}" ALTER COLUMN "${colName}" SET DEFAULT ${colDefaultWithQuotes};`; + } else { + // ALTER TABLE
ALTER COLUMN DROP DEFAULT; + columnDefaultUpQuery = `${ALTER_COL} DROP DEFAULT;`; + } + + if (originalColDefault !== '') { + columnDefaultDownQuery = `ALTER TABLE ONLY "${currentSchema}"."${tableName}" ALTER COLUMN "${colName}" SET DEFAULT ${originalColDefaultWithQuotes};`; + } else { + // there was no default value originally. so drop default. + columnDefaultDownQuery = `ALTER TABLE ONLY "${currentSchema}"."${tableName}" ALTER COLUMN "${colName}" DROP DEFAULT;`; + } + + // check if default is unchanged and then do a drop. if not skip + if (originalColDefault !== colDefault) { + migration.add( + getRunSqlQuery(columnDefaultUpQuery), + getRunSqlQuery(columnDefaultDownQuery) + ); + } + + /* column nullable up/down migration */ + if (nullable) { + // ALTER TABLE
ALTER COLUMN DROP NOT NULL; + const nullableUpQuery = `${ALTER_COL} DROP NOT NULL;`; + const nullableDownQuery = `${ALTER_COL} SET NOT NULL;`; + // check with original null + if (originalColNullable !== 'YES') { + migration.add( + getRunSqlQuery(nullableUpQuery), + getRunSqlQuery(nullableDownQuery) + ); + } + } else { + // ALTER TABLE
ALTER COLUMN SET NOT NULL; + const nullableUpQuery = `${ALTER_COL} SET NOT NULL;`; + const nullableDownQuery = `${ALTER_COL} DROP NOT NULL;`; + // check with original null + if (originalColNullable !== 'NO') { + migration.add( + getRunSqlQuery(nullableUpQuery), + getRunSqlQuery(nullableDownQuery) + ); + } + } + + /* column unique up/down migration */ + if (unique) { + const uniqueUpQuery = `${ALTER_TABLE} ADD CONSTRAINT "${tableName}_${colName}_key" UNIQUE ("${colName}")`; + const uniqueDownQuery = `${ALTER_TABLE} DROP CONSTRAINT "${tableName}_${colName}_key"`; + // check with original unique + if (!originalColUnique) { + migration.add( + getRunSqlQuery(uniqueUpQuery), + getRunSqlQuery(uniqueDownQuery) + ); + } + } else { + const uniqueDownQuery = `${ALTER_TABLE} ADD CONSTRAINT "${tableName}_${colName}_key" UNIQUE ("${colName}")`; + const uniqueUpQuery = `${ALTER_TABLE} DROP CONSTRAINT "${tableName}_${colName}_key"`; + // check with original unique + if (originalColUnique) { + migration.add( + getRunSqlQuery(uniqueUpQuery), + getRunSqlQuery(uniqueDownQuery) + ); + } + } + + /* column comment up/down migration */ + const columnCommentUpQuery = `COMMENT ON COLUMN "${currentSchema}"."${tableName}"."${colName}" IS ${sqlEscapeText( + comment + )}`; + + const columnCommentDownQuery = `COMMENT ON COLUMN "${currentSchema}"."${tableName}"."${colName}" IS ${sqlEscapeText( + originalColComment + )}`; + + // check if comment is unchanged and then do an update. if not skip + if (originalColComment !== comment) { + migration.add( + getRunSqlQuery(columnCommentUpQuery), + getRunSqlQuery(columnCommentDownQuery) + ); + } + + /* rename column */ + if (newName && colName !== newName) { + if (!gqlPattern.test(newName)) { + onInvalidGqlColName(); + } + migration.add( + getRunSqlQuery( + `${ALTER_TABLE} rename column "${colName}" to "${newName}";` + ), + getRunSqlQuery( + `${ALTER_TABLE} rename column "${newName}" to "${colName}";` + ) + ); + } + const migrationName = `alter_table_${currentSchema}_${tableName}_alter_column_${colName}`; + return { + migrationName, + migration, + }; +}; +export const getDownQueryComments = (upqueries: RunSqlType[]) => { + if (Array.isArray(upqueries) && upqueries.length >= 0) { + let comment = `-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below:`; + comment = upqueries.reduce( + (acc, i) => `${acc} +-- ${i.args.sql}`, + comment + ); + return [getRunSqlQuery(comment)]; + } + // all other errors + return []; +};