diff --git a/packages/nocodb/src/version-upgrader/ncXcdbCreatedAndUpdatedSystemFieldsUpgrader.ts b/packages/nocodb/src/version-upgrader/ncXcdbCreatedAndUpdatedSystemFieldsUpgrader.ts index 9c2cbe7fe0..45bc68e762 100644 --- a/packages/nocodb/src/version-upgrader/ncXcdbCreatedAndUpdatedSystemFieldsUpgrader.ts +++ b/packages/nocodb/src/version-upgrader/ncXcdbCreatedAndUpdatedSystemFieldsUpgrader.ts @@ -15,8 +15,6 @@ import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2'; import getColumnUiType from '~/helpers/getColumnUiType'; import RequestQueue from '~/utils/RequestQueue'; -// Example Usage: - // An upgrader for upgrading created_at and updated_at columns // to system column and convert to new uidt CreatedTime and LastModifiedTime @@ -26,6 +24,11 @@ const logger = { `[ncXcdbCreatedAndUpdatedSystemFieldsUpgrader ${Date.now()}] ` + message, ); }, + error: (message: string) => { + console.error( + `[ncXcdbCreatedAndUpdatedSystemFieldsUpgrader ${Date.now()}] ` + message, + ); + }, }; /* Enable if planning to remove trigger @@ -70,143 +73,202 @@ async function upgradeModels({ await Promise.all( models.map(async (model: any) => { if (model.mm) return; + try { + logger.log( + `Upgrading model '${model.title}'(${model.id}) from base '${base.title}'(${base.id}})`, + ); - logger.log( - `Upgrading model '${model.title}'(${model.id}) from base '${base.title}'(${base.id}})`, - ); - - const columns = await model.getColumns(ncMeta); - const oldColumns = columns.map((c) => ({ ...c, cn: c.column_name })); - let isCreatedTimeExists = false; - let isLastModifiedTimeExists = false; - let isCreatedByExists = false; - let isLastModifiedByExists = false; - for (const column of columns) { - if ( - ![ - UITypes.DateTime, - UITypes.CreatedTime, - UITypes.LastModifiedTime, - UITypes.CreatedBy, - UITypes.LastModifiedBy, - ].includes(column.uidt) - ) - continue; - - if (column.uidt === UITypes.CreatedBy && column.system) { - isCreatedByExists = true; - continue; - } - - if (column.uidt === UITypes.LastModifiedBy && column.system) { - isLastModifiedByExists = true; - continue; + const columns = await model.getColumns(ncMeta); + const oldColumns = columns.map((c) => ({ ...c, cn: c.column_name })); + let isCreatedTimeExists = false; + let isLastModifiedTimeExists = false; + let isCreatedByExists = false; + let isLastModifiedByExists = false; + for (const column of columns) { + if ( + ![ + UITypes.DateTime, + UITypes.CreatedTime, + UITypes.LastModifiedTime, + UITypes.CreatedBy, + UITypes.LastModifiedBy, + ].includes(column.uidt) + ) + continue; + + if (column.uidt === UITypes.CreatedBy && column.system) { + isCreatedByExists = true; + continue; + } + + if (column.uidt === UITypes.LastModifiedBy && column.system) { + isLastModifiedByExists = true; + continue; + } + + if ( + [UITypes.CreatedBy, UITypes.LastModifiedBy].includes(column.uidt) + ) { + continue; + } + + if (column.uidt === UITypes.CreatedTime && column.system) { + isCreatedTimeExists = true; + continue; + } + + if (column.uidt === UITypes.LastModifiedTime && column.system) { + isLastModifiedTimeExists = true; + continue; + } + + // if column is created_at or updated_at, update the uidt in meta + if (column.column_name === 'created_at') { + isCreatedTimeExists = true; + await Column.update( + column.id, + { + ...column, + uidt: UITypes.CreatedTime, + system: true, + }, + ncMeta, + true, + ); + + /* Enable if planning to remove trigger + if (source.type === 'pg') { + // delete pg trigger if exists + await deletePgTrigger({ column, ncMeta, model }); + } + */ + } + if (column.column_name === 'updated_at') { + isLastModifiedTimeExists = true; + await Column.update( + column.id, + { + ...column, + uidt: UITypes.LastModifiedTime, + system: true, + cdf: '', + au: false, + }, + ncMeta, + true, + ); + } } - if ([UITypes.CreatedBy, UITypes.LastModifiedBy].includes(column.uidt)) { - continue; - } + // get existing columns from database + const sqlClient = await NcConnectionMgrv2.getSqlClient( + source, + ncMeta.knex, + ); - if (column.uidt === UITypes.CreatedTime && column.system) { - isCreatedTimeExists = true; - continue; - } + const dbColumns = + ( + await sqlClient.columnList({ + tn: model.table_name, + schema: source.getConfig()?.schema, + }) + )?.data?.list?.map((c) => ({ ...c, column_name: c.cn })) || []; - if (column.uidt === UITypes.LastModifiedTime && column.system) { - isLastModifiedTimeExists = true; - continue; + // if no columns found skip since table might not be there + if (!dbColumns.length) { + logger.log( + `Skipping upgrade of model '${model.title}'(${model.id}) from base '${base.title}'(${base.id}}) since columns not found`, + ); + return; } - // if column is created_at or updated_at, update the uidt in meta - if (column.column_name === 'created_at') { - isCreatedTimeExists = true; - await Column.update( - column.id, - { - ...column, + // create created_at & updated_at and created_by & updated_by columns + const newColumns = []; + const existingDbColumns = []; + + if (!isCreatedTimeExists) { + // check column exist and add to meta if found + const columnName = getUniqueColumnName(columns, 'created_at'); + const dbColumn = dbColumns.find((c) => c.cn === columnName); + + // if column already exist, just update the meta + if ( + dbColumn && + getColumnUiType(source, dbColumn) === UITypes.DateTime + ) { + existingDbColumns.push({ + ...dbColumn, uidt: UITypes.CreatedTime, + column_name: columnName, + title: getUniqueColumnAliasName(columns, 'CreatedAt'), system: true, - }, - ncMeta, - true, - ); - - /* Enable if planning to remove trigger - if (source.type === 'pg') { - // delete pg trigger if exists - await deletePgTrigger({ column, ncMeta, model }); - }*/ - } - if (column.column_name === 'updated_at') { - isLastModifiedTimeExists = true; - await Column.update( - column.id, - { - ...column, - uidt: UITypes.LastModifiedTime, + }); + } else { + newColumns.push({ + ...(await getColumnPropsFromUIDT( + { + uidt: UITypes.CreatedTime, + column_name: getUniqueColumnName( + [...columns, ...dbColumns], + 'created_at', + ), + title: getUniqueColumnAliasName(columns, 'CreatedAt'), + }, + source, + )), + cdf: null, system: true, - cdf: '', - au: false, - }, - ncMeta, - true, - ); + altered: Altered.NEW_COLUMN, + }); + } } - } - // get existing columns from database - const sqlClient = await NcConnectionMgrv2.getSqlClient( - source, - ncMeta.knex, - ); - - const dbColumns = - ( - await sqlClient.columnList({ - tn: model.table_name, - schema: source.getConfig()?.schema, - }) - )?.data?.list?.map((c) => ({ ...c, column_name: c.cn })) || []; + if (!isLastModifiedTimeExists) { + const columnName = getUniqueColumnName(columns, 'created_at'); + const dbColumn = dbColumns.find((c) => c.cn === columnName); - // if no columns found skip since table might not be there - if (!dbColumns.length) { - logger.log( - `Skipping upgrade of model '${model.title}'(${model.id}) from base '${base.title}'(${base.id}}) since columns not found`, - ); - return; - } + // if column already exist, just update the meta + if ( + dbColumn && + getColumnUiType(source, dbColumn) === UITypes.DateTime + ) { + existingDbColumns.push({ + uidt: UITypes.LastModifiedTime, + ...dbColumn, + column_name: columnName, + title: getUniqueColumnAliasName(columns, 'UpdatedAt'), + system: true, + }); + } else { + newColumns.push({ + ...(await getColumnPropsFromUIDT( + { + uidt: UITypes.LastModifiedTime, + column_name: getUniqueColumnName( + [...columns, ...dbColumns], + 'updated_at', + ), + title: getUniqueColumnAliasName(columns, 'UpdatedAt'), + }, + source, + )), + cdf: null, + system: true, + altered: Altered.NEW_COLUMN, + }); + } + } - // create created_at & updated_at and created_by & updated_by columns - const newColumns = []; - const existingDbColumns = []; - - if (!isCreatedTimeExists) { - // check column exist and add to meta if found - const columnName = getUniqueColumnName(columns, 'created_at'); - const dbColumn = dbColumns.find((c) => c.cn === columnName); - - // if column already exist, just update the meta - if ( - dbColumn && - getColumnUiType(source, dbColumn) === UITypes.DateTime - ) { - existingDbColumns.push({ - ...dbColumn, - uidt: UITypes.CreatedTime, - column_name: columnName, - title: getUniqueColumnAliasName(columns, 'CreatedAt'), - system: true, - }); - } else { + if (!isCreatedByExists) { newColumns.push({ ...(await getColumnPropsFromUIDT( { - uidt: UITypes.CreatedTime, + uidt: UITypes.CreatedBy, column_name: getUniqueColumnName( [...columns, ...dbColumns], - 'created_at', + 'created_by', ), - title: getUniqueColumnAliasName(columns, 'CreatedAt'), + title: getUniqueColumnAliasName(columns, 'nc_created_by'), }, source, )), @@ -215,34 +277,17 @@ async function upgradeModels({ altered: Altered.NEW_COLUMN, }); } - } - if (!isLastModifiedTimeExists) { - const columnName = getUniqueColumnName(columns, 'created_at'); - const dbColumn = dbColumns.find((c) => c.cn === columnName); - - // if column already exist, just update the meta - if ( - dbColumn && - getColumnUiType(source, dbColumn) === UITypes.DateTime - ) { - existingDbColumns.push({ - uidt: UITypes.LastModifiedTime, - ...dbColumn, - column_name: columnName, - title: getUniqueColumnAliasName(columns, 'UpdatedAt'), - system: true, - }); - } else { + if (!isLastModifiedByExists) { newColumns.push({ ...(await getColumnPropsFromUIDT( { - uidt: UITypes.LastModifiedTime, + uidt: UITypes.LastModifiedBy, column_name: getUniqueColumnName( [...columns, ...dbColumns], - 'updated_at', + 'updated_by', ), - title: getUniqueColumnAliasName(columns, 'UpdatedAt'), + title: getUniqueColumnAliasName(columns, 'nc_updated_by'), }, source, )), @@ -251,78 +296,45 @@ async function upgradeModels({ altered: Altered.NEW_COLUMN, }); } - } - - if (!isCreatedByExists) { - newColumns.push({ - ...(await getColumnPropsFromUIDT( - { - uidt: UITypes.CreatedBy, - column_name: getUniqueColumnName( - [...columns, ...dbColumns], - 'created_by', - ), - title: getUniqueColumnAliasName(columns, 'nc_created_by'), - }, - source, - )), - cdf: null, - system: true, - altered: Altered.NEW_COLUMN, - }); - } - if (!isLastModifiedByExists) { - newColumns.push({ - ...(await getColumnPropsFromUIDT( + // alter table and add new columns if any + if (newColumns.length) { + logger.log( + `Altering table '${model.title}'(${model.id}) from base '${base.title}'(${base.id}}) for new columns`, + ); + // update column in db + const tableUpdateBody = { + ...model, + tn: model.table_name, + originalColumns: oldColumns, + columns: [...columns, ...newColumns].map((c) => ({ + ...c, + cn: c.column_name, + })), + }; + const sqlMgr = ProjectMgrv2.getSqlMgr({ id: source.base_id }, ncMeta); + await sqlMgr.sqlOpPlus(source, 'tableUpdate', tableUpdateBody); + } + for (const newColumn of [...existingDbColumns, ...newColumns]) { + await Column.insert( { - uidt: UITypes.LastModifiedBy, - column_name: getUniqueColumnName( - [...columns, ...dbColumns], - 'updated_by', - ), - title: getUniqueColumnAliasName(columns, 'nc_updated_by'), + ...newColumn, + system: 1, + fk_model_id: model.id, }, - source, - )), - cdf: null, - system: true, - altered: Altered.NEW_COLUMN, - }); - } + ncMeta, + ); + } - // alter table and add new columns if any - if (newColumns.length) { logger.log( - `Altering table '${model.title}'(${model.id}) from base '${base.title}'(${base.id}}) for new columns`, + `Upgraded model '${model.title}'(${model.id}) from base '${base.title}'(${base.id}})`, ); - // update column in db - const tableUpdateBody = { - ...model, - tn: model.table_name, - originalColumns: oldColumns, - columns: [...columns, ...newColumns].map((c) => ({ - ...c, - cn: c.column_name, - })), - }; - const sqlMgr = ProjectMgrv2.getSqlMgr({ id: source.base_id }, ncMeta); - await sqlMgr.sqlOpPlus(source, 'tableUpdate', tableUpdateBody); - } - for (const newColumn of [...existingDbColumns, ...newColumns]) { - await Column.insert( - { - ...newColumn, - system: 1, - fk_model_id: model.id, - }, - ncMeta, + } catch (e) { + logger.error( + `Upgrading model '${model.title}'(${model.id}) from base '${base.title}'(${base.id}}) failed`, ); + throw e; } - - logger.log( - `Upgraded model '${model.title}'(${model.id}) from base '${base.title}'(${base.id}})`, - ); }), ); } @@ -373,7 +385,7 @@ export default async function ({ ncMeta }: NcUpgraderCtx) { })`, ); - return upgradeModels({ ncMeta, source, base }).then(() => { + await upgradeModels({ ncMeta, source, base }).then(() => { logger.log( `Upgraded base '${base.title}'(${base.id},${source.id}) (${i + 1}/${ sources.length