diff --git a/packages/nc-gui/composables/useTable.ts b/packages/nc-gui/composables/useTable.ts index cb4bccea52..4596c2ff2c 100644 --- a/packages/nc-gui/composables/useTable.ts +++ b/packages/nc-gui/composables/useTable.ts @@ -30,7 +30,7 @@ export function useTable(onTableCreate?: (tableMeta: TableType) => void, baseId? const { getMeta, removeMeta } = useMetas() - const { loadTables } = useProject() + const { loadTables, isXcdbBase } = useProject() const { closeTab } = useTabs() const projectStore = useProject() @@ -88,7 +88,9 @@ export function useTable(onTableCreate?: (tableMeta: TableType) => void, baseId? const meta = (await getMeta(table.id as string, true)) as TableType const relationColumns = meta?.columns?.filter((c) => c.uidt === UITypes.LinkToAnotherRecord && !isSystemColumn(c)) - if (relationColumns?.length) { + // Check if table has any relation columns and show notification + // skip for xcdb base + if (relationColumns?.length && !isXcdbBase(table.base_id)) { const refColMsgs = await Promise.all( relationColumns.map(async (c, i) => { const refMeta = (await getMeta( diff --git a/packages/nocodb/src/db/sql-client/lib/sqlite/SqliteClient.ts b/packages/nocodb/src/db/sql-client/lib/sqlite/SqliteClient.ts index 118423df7b..49e078a1b0 100644 --- a/packages/nocodb/src/db/sql-client/lib/sqlite/SqliteClient.ts +++ b/packages/nocodb/src/db/sql-client/lib/sqlite/SqliteClient.ts @@ -22,7 +22,7 @@ class SqliteClient extends KnexClient { // sqlite does not support inserting default values and knex fires a warning without this flag connectionConfig.connection.useNullAsDefault = true; super(connectionConfig); - this.sqlClient = knex(connectionConfig.connection); + this.sqlClient = connectionConfig?.knex || knex(connectionConfig.connection); this.queries = queries; this._version = {}; } diff --git a/packages/nocodb/src/db/sql-mgr/v2/ProjectMgrv2.ts b/packages/nocodb/src/db/sql-mgr/v2/ProjectMgrv2.ts index f8f884ea5f..730cbdda59 100644 --- a/packages/nocodb/src/db/sql-mgr/v2/ProjectMgrv2.ts +++ b/packages/nocodb/src/db/sql-mgr/v2/ProjectMgrv2.ts @@ -1,18 +1,21 @@ -import SqlMgrv2 from './SqlMgrv2'; -import SqlMgrv2Trans from './SqlMgrv2Trans'; +import { MetaService } from '../../../meta/meta.service' +import SqlMgrv2 from './SqlMgrv2' +import SqlMgrv2Trans from './SqlMgrv2Trans' // import type NcMetaIO from '../../../meta/NcMetaIO'; -import type Base from '../../../models/Base'; +import type Base from '../../../models/Base' export default class ProjectMgrv2 { private static sqlMgrMap: { [key: string]: SqlMgrv2; - } = {}; + } = {} + + public static getSqlMgr(project: { id: string }, ncMeta: MetaService = null): SqlMgrv2 { + if (ncMeta) return new SqlMgrv2(project, ncMeta) - public static getSqlMgr(project: { id: string }): SqlMgrv2 { if (!this.sqlMgrMap[project.id]) { - this.sqlMgrMap[project.id] = new SqlMgrv2(project); + this.sqlMgrMap[project.id] = new SqlMgrv2(project) } - return this.sqlMgrMap[project.id]; + return this.sqlMgrMap[project.id] } public static async getSqlMgrTrans( @@ -21,8 +24,8 @@ export default class ProjectMgrv2 { ncMeta: any, base: Base, ): Promise { - const sqlMgr = new SqlMgrv2Trans(project, ncMeta, base); - await sqlMgr.startTransaction(base); - return sqlMgr; + const sqlMgr = new SqlMgrv2Trans(project, ncMeta, base) + await sqlMgr.startTransaction(base) + return sqlMgr } } diff --git a/packages/nocodb/src/db/sql-mgr/v2/SqlMgrv2.ts b/packages/nocodb/src/db/sql-mgr/v2/SqlMgrv2.ts index 17d808e15e..437a79921f 100644 --- a/packages/nocodb/src/db/sql-mgr/v2/SqlMgrv2.ts +++ b/packages/nocodb/src/db/sql-mgr/v2/SqlMgrv2.ts @@ -5,12 +5,14 @@ import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2'; import SqlClientFactory from '../../sql-client/lib/SqlClientFactory'; import KnexMigratorv2 from '../../sql-migrator/lib/KnexMigratorv2'; import Debug from '../../util/Debug'; +import type { MetaService } from '../../../meta/meta.service'; import type Base from '../../../models/Base'; const log = new Debug('SqlMgr'); export default class SqlMgrv2 { protected _migrator: KnexMigratorv2; + protected ncMeta?: MetaService; // @ts-ignore private currentProjectFolder: any; @@ -20,18 +22,18 @@ export default class SqlMgrv2 { * @param {String} args.toolDbPath - path to sqlite file that sql mgr will use * @memberof SqlMgr */ - constructor(args: { id: string }) { + constructor(args: { id: string }, ncMeta = null) { const func = 'constructor'; log.api(`${func}:args:`, args); // this.metaDb = args.metaDb; this._migrator = new KnexMigratorv2(args); - - return this; + this.ncMeta = ncMeta; } public async migrator(_base: Base) { return this._migrator; } + public static async testConnection(args = {}) { const client = await SqlClientFactory.create(args); return client.testConnection(); @@ -119,6 +121,10 @@ export default class SqlMgrv2 { } protected async getSqlClient(base: Base) { + if (base.is_meta && this.ncMeta) { + return NcConnectionMgrv2.getSqlClient(base, this.ncMeta.knex); + } + return NcConnectionMgrv2.getSqlClient(base); } } diff --git a/packages/nocodb/src/meta/meta.service.ts b/packages/nocodb/src/meta/meta.service.ts index 5d30444980..565b921b42 100644 --- a/packages/nocodb/src/meta/meta.service.ts +++ b/packages/nocodb/src/meta/meta.service.ts @@ -532,7 +532,6 @@ export class MetaService { } else { query.where(idOrCondition); } - return query.first(); } diff --git a/packages/nocodb/src/models/Model.ts b/packages/nocodb/src/models/Model.ts index 0826fd538a..9fdbf44d4b 100644 --- a/packages/nocodb/src/models/Model.ts +++ b/packages/nocodb/src/models/Model.ts @@ -368,10 +368,10 @@ export default class Model implements TableType { } async delete(ncMeta = Noco.ncMeta, force = false): Promise { - await Audit.deleteRowComments(this.id); + await Audit.deleteRowComments(this.id, ncMeta); - for (const view of await this.getViews(true)) { - await view.delete(); + for (const view of await this.getViews(true, ncMeta)) { + await view.delete(ncMeta); } for (const col of await this.getColumns(ncMeta)) { diff --git a/packages/nocodb/src/models/View.ts b/packages/nocodb/src/models/View.ts index 86abcae743..e424200c64 100644 --- a/packages/nocodb/src/models/View.ts +++ b/packages/nocodb/src/models/View.ts @@ -986,9 +986,9 @@ export default class View implements ViewType { // @ts-ignore static async delete(viewId, ncMeta = Noco.ncMeta) { - const view = await this.get(viewId); - await Sort.deleteAll(viewId); - await Filter.deleteAll(viewId); + const view = await this.get(viewId, ncMeta); + await Sort.deleteAll(viewId, ncMeta); + await Filter.deleteAll(viewId, ncMeta); const table = this.extractViewTableName(view); const tableScope = this.extractViewTableNameScope(view); const columnTable = this.extractViewColumnsTableName(view); @@ -1258,8 +1258,8 @@ export default class View implements ViewType { ); } - async delete() { - await View.delete(this.id); + async delete(ncMeta = Noco.ncMeta){ + await View.delete(this.id, ncMeta); } static async shareViewList(tableId, ncMeta = Noco.ncMeta) { diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index 7fd2972fce..f1c8a81620 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -1157,7 +1157,10 @@ export class ColumnsService { ); const base = await Base.get(table.base_id, ncMeta); - const sqlMgr = await ProjectMgrv2.getSqlMgr({ id: base.project_id }); + const sqlMgr = await ProjectMgrv2.getSqlMgr( + { id: base.project_id }, + ncMeta, + ); switch (column.uidt) { case UITypes.Lookup: @@ -1271,7 +1274,7 @@ export class ColumnsService { const colOpt = await c.getColOptions(ncMeta); if (colOpt.fk_related_model_id === mmTable.id) { - await Column.delete(c.id); + await Column.delete(c.id, ncMeta); } } @@ -1403,35 +1406,37 @@ export class ColumnsService { if (!relationColOpt) { foreignKeyName = ( ( - await childTable.getColumns(ncMeta).then((cols) => { - return cols?.find((c) => { - return ( - c.uidt === UITypes.LinkToAnotherRecord && - c.colOptions.fk_related_model_id === parentTable.id && - (c.colOptions as LinkToAnotherRecordType).fk_child_column_id === - childColumn.id && - (c.colOptions as LinkToAnotherRecordType) - .fk_parent_column_id === parentColumn.id - ); - }); + await childTable.getColumns(ncMeta).then(async (cols) => { + for (const col of cols) { + if (col.uidt === UITypes.LinkToAnotherRecord) { + const colOptions = + await col.getColOptions(ncMeta); + console.log(colOptions); + if (colOptions.fk_related_model_id === parentTable.id) { + return { colOptions }; + } + } + } }) - ).colOptions as LinkToAnotherRecordType + )?.colOptions as LinkToAnotherRecordType ).fk_index_name; } else { foreignKeyName = relationColOpt.fk_index_name; } - // todo: handle relation delete exception - try { - await sqlMgr.sqlOpPlus(base, 'relationDelete', { - childColumn: childColumn.column_name, - childTable: childTable.table_name, - parentTable: parentTable.table_name, - parentColumn: parentColumn.column_name, - foreignKeyName, - }); - } catch (e) { - console.log(e); + if (!relationColOpt?.virtual) { + // todo: handle relation delete exception + try { + await sqlMgr.sqlOpPlus(base, 'relationDelete', { + childColumn: childColumn.column_name, + childTable: childTable.table_name, + parentTable: parentTable.table_name, + parentColumn: parentColumn.column_name, + foreignKeyName, + }); + } catch (e) { + console.log(e); + } } if (!relationColOpt) return; @@ -1462,6 +1467,28 @@ export class ColumnsService { }, ncMeta, ); + + // if virtual column delete all index before deleting the column + if (relationColOpt?.virtual) { + const indexes = + ( + await sqlMgr.sqlOp(base, 'indexList', { + tn: cTable.table_name, + }) + )?.data?.list ?? []; + + for (const index of indexes) { + if (index.cn !== childColumn.column_name) continue; + + await sqlMgr.sqlOpPlus(base, 'indexDelete', { + ...index, + tn: cTable.table_name, + columns: [childColumn.column_name], + indexName: index.index_name, + }); + } + } + const tableUpdateBody = { ...cTable, tn: cTable.table_name, @@ -1511,6 +1538,12 @@ export class ColumnsService { const sqlMgr = await ProjectMgrv2.getSqlMgr({ id: param.base.project_id, }); + + // if xcdb base then treat as virtual relation to avoid creating foreign key + if (param.base.is_meta) { + (param.column as LinkToAnotherColumnReqType).virtual = true; + } + if ( (param.column as LinkToAnotherColumnReqType).type === 'hm' || (param.column as LinkToAnotherColumnReqType).type === 'bt' @@ -1565,11 +1598,6 @@ export class ColumnsService { childColumn = await Column.get({ colId: id }); - // if xcdb base then treat as virtual relation to avoid creating foreign key - if (param.base.is_meta) { - (param.column as LinkToAnotherColumnReqType).virtual = true; - } - // ignore relation creation if virtual if (!(param.column as LinkToAnotherColumnReqType).virtual) { foreignKeyName = generateFkName(parent, child); @@ -1746,6 +1774,7 @@ export class ColumnsService { fk_mm_child_column_id: childCol.id, fk_mm_parent_column_id: parentCol.id, fk_related_model_id: parent.id, + virtual: (param.column as LinkToAnotherColumnReqType).virtual, }); await Column.insert({ title: getUniqueColumnAliasName( @@ -1765,6 +1794,7 @@ export class ColumnsService { fk_mm_child_column_id: parentCol.id, fk_mm_parent_column_id: childCol.id, fk_related_model_id: child.id, + virtual: (param.column as LinkToAnotherColumnReqType).virtual, }); // todo: create index for virtual relations as well diff --git a/packages/nocodb/src/services/tables.service.ts b/packages/nocodb/src/services/tables.service.ts index 4d9b21ca76..cf005b04a9 100644 --- a/packages/nocodb/src/services/tables.service.ts +++ b/packages/nocodb/src/services/tables.service.ts @@ -184,6 +184,11 @@ export class TablesService { // delete all relations await Promise.all( relationColumns.map((c) => { + // skip if column is hasmany relation to mm table + if (c.system) { + return; + } + return this.columnsService.columnDelete( { req: param.req, @@ -194,7 +199,7 @@ export class TablesService { }), ); - const sqlMgr = await ProjectMgrv2.getSqlMgr(project); + const sqlMgr = await ProjectMgrv2.getSqlMgr(project, ncMeta); (table as any).tn = table.table_name; table.columns = table.columns.filter((c) => !isVirtualCol(c)); table.columns.forEach((c) => { @@ -210,15 +215,18 @@ export class TablesService { }); } - await Audit.insert({ - project_id: project.id, - base_id: base.id, - op_type: AuditOperationTypes.TABLE, - op_sub_type: AuditOperationSubTypes.DELETE, - user: param.user?.email, - description: `Deleted ${table.type} ${table.table_name} with alias ${table.title} `, - ip: param.req?.clientIp, - }).then(() => {}); + await Audit.insert( + { + project_id: project.id, + base_id: base.id, + op_type: AuditOperationTypes.TABLE, + op_sub_type: AuditOperationSubTypes.DELETE, + user: param.user?.email, + description: `Deleted ${table.type} ${table.table_name} with alias ${table.title} `, + ip: param.req?.clientIp, + }, + ncMeta, + ).then(() => {}); T.emit('evt', { evt_type: 'table:deleted' });