diff --git a/packages/@n8n/api-types/src/index.ts b/packages/@n8n/api-types/src/index.ts index 381f1ef469d..ba8e7e195a2 100644 --- a/packages/@n8n/api-types/src/index.ts +++ b/packages/@n8n/api-types/src/index.ts @@ -53,6 +53,5 @@ export { type DataStoreCreateColumnSchema, type DataStoreListFilter, type DataStoreListOptions, - type DataStoreUserTableName, dateTimeSchema, } from './schemas/data-store.schema'; diff --git a/packages/@n8n/api-types/src/schemas/data-store.schema.ts b/packages/@n8n/api-types/src/schemas/data-store.schema.ts index af8a4d9a192..56e4924c969 100644 --- a/packages/@n8n/api-types/src/schemas/data-store.schema.ts +++ b/packages/@n8n/api-types/src/schemas/data-store.schema.ts @@ -39,8 +39,6 @@ export const dataStoreSchema = z.object({ export type DataStore = z.infer; export type DataStoreColumn = z.infer; -export type DataStoreUserTableName = `data_store_user_${string}`; - export type DataStoreListFilter = { id?: string | string[]; projectId?: string | string[]; diff --git a/packages/cli/src/modules/data-store/__tests__/data-store.controller.test.ts b/packages/cli/src/modules/data-store/__tests__/data-store.controller.test.ts index 6e138442b0b..8befd42d68b 100644 --- a/packages/cli/src/modules/data-store/__tests__/data-store.controller.test.ts +++ b/packages/cli/src/modules/data-store/__tests__/data-store.controller.test.ts @@ -18,7 +18,6 @@ import * as utils from '@test-integration/utils'; import { DataStoreColumnRepository } from '../data-store-column.repository'; import { DataStoreRowsRepository } from '../data-store-rows.repository'; import { DataStoreRepository } from '../data-store.repository'; -import { toTableName } from '../utils/sql-utils'; let owner: User; let member: User; @@ -781,9 +780,9 @@ describe('DELETE /projects/:projectId/data-stores/:dataStoreId', () => { }); expect(dataStoreColumnInDb).toBeNull(); - await expect( - dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}), - ).rejects.toThrow(QueryFailedError); + await expect(dataStoreRowsRepository.getManyAndCount(dataStore.id, {})).rejects.toThrow( + QueryFailedError, + ); }); }); @@ -1839,7 +1838,7 @@ describe('POST /projects/:projectId/data-stores/:dataStoreId/insert', () => { data: [1], }); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}); + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); expect(rowsInDb.count).toBe(1); expect(rowsInDb.data[0]).toMatchObject(payload.data[0]); }); @@ -1879,7 +1878,7 @@ describe('POST /projects/:projectId/data-stores/:dataStoreId/insert', () => { data: [1], }); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}); + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); expect(rowsInDb.count).toBe(1); expect(rowsInDb.data[0]).toMatchObject(payload.data[0]); }); @@ -1916,7 +1915,7 @@ describe('POST /projects/:projectId/data-stores/:dataStoreId/insert', () => { data: [1], }); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}); + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); expect(rowsInDb.count).toBe(1); expect(rowsInDb.data[0]).toMatchObject(payload.data[0]); }); @@ -1950,7 +1949,7 @@ describe('POST /projects/:projectId/data-stores/:dataStoreId/insert', () => { .expect(400); expect(response.body.message).toContain('unknown column'); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}); + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); expect(rowsInDb.count).toBe(0); }); @@ -2287,7 +2286,7 @@ describe('DELETE /projects/:projectId/data-stores/:dataStoreId/rows', () => { .query({ ids: '1' }) .expect(403); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}); + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); expect(rowsInDb.count).toBe(1); }); @@ -2318,7 +2317,7 @@ describe('DELETE /projects/:projectId/data-stores/:dataStoreId/rows', () => { .query({ ids: '1' }) .expect(403); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}); + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); expect(rowsInDb.count).toBe(1); }); @@ -2358,7 +2357,7 @@ describe('DELETE /projects/:projectId/data-stores/:dataStoreId/rows', () => { .query({ ids: '1,3' }) .expect(200); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}); + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); expect(rowsInDb.count).toBe(1); expect(rowsInDb.data[0]).toMatchObject({ first: 'test value 2', @@ -2398,7 +2397,7 @@ describe('DELETE /projects/:projectId/data-stores/:dataStoreId/rows', () => { .query({ ids: '2' }) .expect(200); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}); + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); expect(rowsInDb.count).toBe(1); expect(rowsInDb.data[0]).toMatchObject({ first: 'test value 1', @@ -2437,7 +2436,7 @@ describe('DELETE /projects/:projectId/data-stores/:dataStoreId/rows', () => { .query({ ids: '1,2' }) .expect(200); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}); + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); expect(rowsInDb.count).toBe(0); }); @@ -2474,7 +2473,7 @@ describe('DELETE /projects/:projectId/data-stores/:dataStoreId/rows', () => { .query({ ids: '2' }) .expect(200); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}); + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); expect(rowsInDb.count).toBe(2); expect(rowsInDb.data.map((r) => r.first).sort()).toEqual(['test value 1', 'test value 3']); }); @@ -2501,7 +2500,7 @@ describe('DELETE /projects/:projectId/data-stores/:dataStoreId/rows', () => { expect(response.body.data).toBe(true); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}); + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); expect(rowsInDb.count).toBe(1); }); @@ -2525,7 +2524,7 @@ describe('DELETE /projects/:projectId/data-stores/:dataStoreId/rows', () => { .query({ ids: '999,1000' }) .expect(200); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}); + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); expect(rowsInDb.count).toBe(1); }); @@ -2552,7 +2551,7 @@ describe('DELETE /projects/:projectId/data-stores/:dataStoreId/rows', () => { .query({ ids: '1,999,2,1000' }) .expect(200); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}); + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); expect(rowsInDb.count).toBe(0); }); }); @@ -2687,7 +2686,7 @@ describe('POST /projects/:projectId/data-stores/:dataStoreId/upsert', () => { .send(payload) .expect(200); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}); + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); expect(rowsInDb.count).toBe(1); expect(rowsInDb.data[0]).toMatchObject(payload.rows[0]); }); @@ -2724,7 +2723,7 @@ describe('POST /projects/:projectId/data-stores/:dataStoreId/upsert', () => { .send(payload) .expect(200); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}); + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); expect(rowsInDb.count).toBe(1); expect(rowsInDb.data[0]).toMatchObject(payload.rows[0]); }); @@ -2758,7 +2757,7 @@ describe('POST /projects/:projectId/data-stores/:dataStoreId/upsert', () => { .send(payload) .expect(200); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}); + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); expect(rowsInDb.count).toBe(1); expect(rowsInDb.data[0]).toMatchObject(payload.rows[0]); }); @@ -2793,7 +2792,7 @@ describe('POST /projects/:projectId/data-stores/:dataStoreId/upsert', () => { .expect(400); expect(response.body.message).toContain('unknown column'); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), {}); + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); expect(rowsInDb.count).toBe(0); }); @@ -2840,7 +2839,7 @@ describe('POST /projects/:projectId/data-stores/:dataStoreId/upsert', () => { .send(payload) .expect(200); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(toTableName(dataStore.id), { + const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, { sortBy: ['id', 'ASC'], }); expect(rowsInDb.count).toBe(3); diff --git a/packages/cli/src/modules/data-store/__tests__/data-store.service.test.ts b/packages/cli/src/modules/data-store/__tests__/data-store.service.test.ts index 1005ae5b430..3ab30170019 100644 --- a/packages/cli/src/modules/data-store/__tests__/data-store.service.test.ts +++ b/packages/cli/src/modules/data-store/__tests__/data-store.service.test.ts @@ -11,7 +11,6 @@ import { DataStoreColumnNotFoundError } from '../errors/data-store-column-not-fo import { DataStoreNameConflictError } from '../errors/data-store-name-conflict.error'; import { DataStoreNotFoundError } from '../errors/data-store-not-found.error'; import { DataStoreValidationError } from '../errors/data-store-validation.error'; -import { toTableName } from '../utils/sql-utils'; beforeAll(async () => { await testModules.loadModules(['data-store']); @@ -73,7 +72,7 @@ describe('dataStore', () => { ]); // Select the column from user table to check for its existence - const userTableName = toTableName(dataStoreId); + const userTableName = dataStoreRowsRepository.toTableName(dataStoreId); const rows = await dataStoreRepository.manager .createQueryBuilder() .select('foo') @@ -96,7 +95,7 @@ describe('dataStore', () => { await expect(dataStoreService.getColumns(dataStoreId, project1.id)).resolves.toEqual([]); - const userTableName = toTableName(dataStoreId); + const userTableName = dataStoreRowsRepository.toTableName(dataStoreId); const queryRunner = dataStoreRepository.manager.connection.createQueryRunner(); try { const table = await queryRunner.getTable(userTableName); @@ -225,7 +224,7 @@ describe('dataStore', () => { // ACT const result = await dataStoreService.deleteDataStore(dataStoreId, project1.id); - const userTableName = toTableName(dataStoreId); + const userTableName = dataStoreRowsRepository.toTableName(dataStoreId); // ASSERT expect(result).toEqual(true); @@ -335,7 +334,7 @@ describe('dataStore', () => { }, ]); - const userTableName = toTableName(dataStoreId); + const userTableName = dataStoreRowsRepository.toTableName(dataStoreId); const queryRunner = dataStoreRepository.manager.connection.createQueryRunner(); try { const table = await queryRunner.getTable(userTableName); @@ -402,7 +401,7 @@ describe('dataStore', () => { }, ]); - const userTableName = toTableName(dataStoreId); + const userTableName = dataStoreRowsRepository.toTableName(dataStoreId); const queryRunner = dataStoreRepository.manager.connection.createQueryRunner(); try { const table = await queryRunner.getTable(userTableName); @@ -1020,10 +1019,7 @@ describe('dataStore', () => { // ASSERT expect(result).toEqual([2]); - const { count, data } = await dataStoreRowsRepository.getManyAndCount( - toTableName(dataStoreId), - {}, - ); + const { count, data } = await dataStoreRowsRepository.getManyAndCount(dataStoreId, {}); expect(count).toEqual(2); expect(data).toEqual([ @@ -1060,10 +1056,7 @@ describe('dataStore', () => { // ASSERT expect(result).toEqual([3, 4]); - const { count, data } = await dataStoreRowsRepository.getManyAndCount( - toTableName(dataStoreId), - {}, - ); + const { count, data } = await dataStoreRowsRepository.getManyAndCount(dataStoreId, {}); expect(count).toEqual(3); expect(data).toEqual([ @@ -1246,10 +1239,7 @@ describe('dataStore', () => { // ASSERT expect(result).toBe(true); - const { count, data } = await dataStoreRowsRepository.getManyAndCount( - toTableName(dataStoreId), - {}, - ); + const { count, data } = await dataStoreRowsRepository.getManyAndCount(dataStoreId, {}); expect(count).toEqual(1); expect(data).toEqual([{ fullName: 'Alicia', age: 31, id: 1, pid: '1995-111a' }]); @@ -1281,10 +1271,7 @@ describe('dataStore', () => { // ASSERT expect(result).toBe(true); - const { count, data } = await dataStoreRowsRepository.getManyAndCount( - toTableName(dataStoreId), - {}, - ); + const { count, data } = await dataStoreRowsRepository.getManyAndCount(dataStoreId, {}); expect(count).toEqual(2); expect(data).toEqual([ diff --git a/packages/cli/src/modules/data-store/data-store-column.repository.ts b/packages/cli/src/modules/data-store/data-store-column.repository.ts index fd7b27be392..e8a3d828f24 100644 --- a/packages/cli/src/modules/data-store/data-store-column.repository.ts +++ b/packages/cli/src/modules/data-store/data-store-column.repository.ts @@ -76,10 +76,16 @@ export class DataStoreColumnRepository extends Repository { async deleteColumn(dataStoreId: string, column: DataStoreColumn) { await this.manager.transaction(async (em) => { await em.remove(DataStoreColumn, column); + + const queryRunner = em.queryRunner; + if (!queryRunner) { + throw new UnexpectedError('QueryRunner is not available'); + } + await this.dataStoreRowsRepository.dropColumnFromTable( dataStoreId, column.name, - em, + queryRunner, em.connection.options.type, ); await this.shiftColumns(dataStoreId, column.index, -1, em); diff --git a/packages/cli/src/modules/data-store/data-store-rows.repository.ts b/packages/cli/src/modules/data-store/data-store-rows.repository.ts index 8e5fc9f42e4..cfc38b9c11d 100644 --- a/packages/cli/src/modules/data-store/data-store-rows.repository.ts +++ b/packages/cli/src/modules/data-store/data-store-rows.repository.ts @@ -1,21 +1,16 @@ import type { ListDataStoreContentQueryDto, ListDataStoreContentFilter, - DataStoreUserTableName, UpsertDataStoreRowsDto, } from '@n8n/api-types'; +import { GlobalConfig } from '@n8n/config'; import { CreateTable, DslColumn } from '@n8n/db'; import { Service } from '@n8n/di'; -import { - DataSource, - DataSourceOptions, - EntityManager, - QueryRunner, - SelectQueryBuilder, -} from '@n8n/typeorm'; +import { DataSource, DataSourceOptions, QueryRunner, SelectQueryBuilder } from '@n8n/typeorm'; import { DataStoreColumnJsType, DataStoreRows } from 'n8n-workflow'; import { DataStoreColumn } from './data-store-column.entity'; +import { DataStoreUserTableName } from './data-store.types'; import { addColumnQuery, buildColumnTypeMap, @@ -26,7 +21,6 @@ import { quoteIdentifier, splitRowsByExistence, toDslColumns, - toTableName, } from './utils/sql-utils'; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -50,14 +44,17 @@ function getConditionAndParams( @Service() export class DataStoreRowsRepository { - constructor(private dataSource: DataSource) {} + constructor( + private dataSource: DataSource, + private readonly globalConfig: GlobalConfig, + ) {} - // TypeORM cannot infer the columns for a dynamic table name, so we use a raw query - async insertRows( - tableName: DataStoreUserTableName, - rows: DataStoreRows, - columns: DataStoreColumn[], - ) { + toTableName(dataStoreId: string): DataStoreUserTableName { + const { tablePrefix } = this.globalConfig.database; + return `${tablePrefix}data_store_user_${dataStoreId}`; + } + + async insertRows(dataStoreId: string, rows: DataStoreRows, columns: DataStoreColumn[]) { const insertedIds: number[] = []; // We insert one by one as the default behavior of returning the last inserted ID @@ -75,7 +72,7 @@ export class DataStoreRowsRepository { .createQueryBuilder() .insert() .into( - tableName, + this.toTableName(dataStoreId), columns.map((c) => c.name), ) .values(row); @@ -92,21 +89,18 @@ export class DataStoreRowsRepository { return insertedIds; } - async upsertRows( - tableName: DataStoreUserTableName, - dto: UpsertDataStoreRowsDto, - columns: DataStoreColumn[], - ) { + // TypeORM cannot infer the columns for a dynamic table name, so we use a raw query + async upsertRows(dataStoreId: string, dto: UpsertDataStoreRowsDto, columns: DataStoreColumn[]) { const { rows, matchFields } = dto; const { rowsToInsert, rowsToUpdate } = await this.fetchAndSplitRowsByExistence( - tableName, + dataStoreId, matchFields, rows, ); if (rowsToInsert.length > 0) { - await this.insertRows(tableName, rowsToInsert, columns); + await this.insertRows(dataStoreId, rowsToInsert, columns); } if (rowsToUpdate.length > 0) { @@ -119,7 +113,7 @@ export class DataStoreRowsRepository { const setData = Object.fromEntries(updateKeys.map((key) => [key, row[key]])); const whereData = Object.fromEntries(matchFields.map((key) => [key, row[key]])); - await this.updateRow(tableName, setData, whereData, columns); + await this.updateRow(dataStoreId, setData, whereData, columns); } } @@ -127,7 +121,7 @@ export class DataStoreRowsRepository { } async updateRow( - tableName: DataStoreUserTableName, + dataStoreId: string, setData: Record, whereData: Record, columns: DataStoreColumn[], @@ -135,7 +129,7 @@ export class DataStoreRowsRepository { const dbType = this.dataSource.options.type; const columnTypeMap = buildColumnTypeMap(columns); - const queryBuilder = this.dataSource.createQueryBuilder().update(tableName); + const queryBuilder = this.dataSource.createQueryBuilder().update(this.toTableName(dataStoreId)); const setValues: Record = {}; for (const [key, value] of Object.entries(setData)) { @@ -153,13 +147,13 @@ export class DataStoreRowsRepository { await queryBuilder.execute(); } - async deleteRows(tableName: DataStoreUserTableName, ids: number[]) { + async deleteRows(dataStoreId: string, ids: number[]) { if (ids.length === 0) { return true; } const dbType = this.dataSource.options.type; - const quotedTableName = quoteIdentifier(tableName, dbType); + const quotedTableName = quoteIdentifier(this.toTableName(dataStoreId), dbType); const placeholders = ids.map((_, index) => getPlaceholder(index + 1, dbType)).join(', '); const query = `DELETE FROM ${quotedTableName} WHERE id IN (${placeholders})`; @@ -168,36 +162,40 @@ export class DataStoreRowsRepository { } async createTableWithColumns( - tableName: string, + dataStoreId: string, columns: DataStoreColumn[], queryRunner: QueryRunner, ) { const dslColumns = [new DslColumn('id').int.autoGenerate2.primary, ...toDslColumns(columns)]; - const createTable = new CreateTable(tableName, '', queryRunner); + const createTable = new CreateTable(this.toTableName(dataStoreId), '', queryRunner); createTable.withColumns.apply(createTable, dslColumns); + await createTable.execute(queryRunner); } + async dropTable(dataStoreId: string, queryRunner: QueryRunner) { + await queryRunner.dropTable(this.toTableName(dataStoreId), true); + } + async addColumn( dataStoreId: string, column: DataStoreColumn, queryRunner: QueryRunner, dbType: DataSourceOptions['type'], ) { - const tableName = toTableName(dataStoreId); - await queryRunner.manager.query(addColumnQuery(tableName, column, dbType)); + await queryRunner.query(addColumnQuery(this.toTableName(dataStoreId), column, dbType)); } async dropColumnFromTable( dataStoreId: string, columnName: string, - em: EntityManager, + queryRunner: QueryRunner, dbType: DataSourceOptions['type'], ) { - await em.query(deleteColumnQuery(toTableName(dataStoreId), columnName, dbType)); + await queryRunner.query(deleteColumnQuery(this.toTableName(dataStoreId), columnName, dbType)); } - async getManyAndCount(dataStoreId: DataStoreUserTableName, dto: ListDataStoreContentQueryDto) { + async getManyAndCount(dataStoreId: string, dto: ListDataStoreContentQueryDto) { const [countQuery, query] = this.getManyQuery(dataStoreId, dto); const data: DataStoreRows = await query.select('*').getRawMany(); const countResult = await countQuery.select('COUNT(*) as count').getRawOne<{ @@ -208,19 +206,19 @@ export class DataStoreRowsRepository { return { count: count ?? -1, data }; } - async getRowIds(dataStoreId: DataStoreUserTableName, dto: ListDataStoreContentQueryDto) { + async getRowIds(dataStoreId: string, dto: ListDataStoreContentQueryDto) { const [_, query] = this.getManyQuery(dataStoreId, dto); const result = await query.select('dataStore.id').getRawMany(); return result; } private getManyQuery( - dataStoreTableName: DataStoreUserTableName, + dataStoreId: string, dto: ListDataStoreContentQueryDto, ): [QueryBuilder, QueryBuilder] { const query = this.dataSource.createQueryBuilder(); - query.from(dataStoreTableName, 'dataStore'); + query.from(this.toTableName(dataStoreId), 'dataStore'); this.applyFilters(query, dto); const countQuery = query.clone().select('COUNT(*)'); this.applySorting(query, dto); @@ -268,7 +266,7 @@ export class DataStoreRowsRepository { } private async fetchAndSplitRowsByExistence( - tableName: string, + dataStoreId: string, matchFields: string[], rows: DataStoreRows, ): Promise<{ rowsToInsert: DataStoreRows; rowsToUpdate: DataStoreRows }> { @@ -287,7 +285,7 @@ export class DataStoreRowsRepository { } const quotedFields = matchFields.map((field) => quoteIdentifier(field, dbType)).join(', '); - const quotedTableName = quoteIdentifier(tableName, dbType); + const quotedTableName = quoteIdentifier(this.toTableName(dataStoreId), dbType); const query = ` SELECT ${quotedFields} diff --git a/packages/cli/src/modules/data-store/data-store.repository.ts b/packages/cli/src/modules/data-store/data-store.repository.ts index 89f984f1c60..da51e8292e4 100644 --- a/packages/cli/src/modules/data-store/data-store.repository.ts +++ b/packages/cli/src/modules/data-store/data-store.repository.ts @@ -10,7 +10,6 @@ import { UnexpectedError } from 'n8n-workflow'; import { DataStoreColumn } from './data-store-column.entity'; import { DataStoreRowsRepository } from './data-store-rows.repository'; import { DataStore } from './data-store.entity'; -import { toTableName } from './utils/sql-utils'; @Service() export class DataStoreRepository extends Repository { @@ -32,7 +31,6 @@ export class DataStoreRepository extends Repository { await em.insert(DataStore, dataStore); dataStoreId = dataStore.id; - const tableName = toTableName(dataStore.id); const queryRunner = em.queryRunner; if (!queryRunner) { throw new UnexpectedError('QueryRunner is not available'); @@ -41,9 +39,9 @@ export class DataStoreRepository extends Repository { // insert columns const columnEntities = columns.map((col, index) => em.create(DataStoreColumn, { + dataStoreId, name: col.name, type: col.type, - dataStoreId: dataStore.id, index: col.index ?? index, }), ); @@ -54,7 +52,7 @@ export class DataStoreRepository extends Repository { // create user table (will create empty table with just id column if no columns) await this.dataStoreRowsRepository.createTableWithColumns( - tableName, + dataStoreId, columnEntities, queryRunner, ); @@ -81,7 +79,7 @@ export class DataStoreRepository extends Repository { } await em.delete(DataStore, { id: dataStoreId }); - await queryRunner.dropTable(toTableName(dataStoreId), true); + await this.dataStoreRowsRepository.dropTable(dataStoreId, queryRunner); return true; }); @@ -113,7 +111,7 @@ export class DataStoreRepository extends Repository { let changed = false; for (const match of existingTables) { const result = await em.delete(DataStore, { id: match.id }); - await queryRunner.dropTable(toTableName(match.id), true); + await this.dataStoreRowsRepository.dropTable(match.id, queryRunner); changed = changed || (result.affected ?? 0) > 0; } diff --git a/packages/cli/src/modules/data-store/data-store.service.ts b/packages/cli/src/modules/data-store/data-store.service.ts index 7ae8d996b51..a61885c4810 100644 --- a/packages/cli/src/modules/data-store/data-store.service.ts +++ b/packages/cli/src/modules/data-store/data-store.service.ts @@ -20,7 +20,7 @@ import { DataStoreColumnNotFoundError } from './errors/data-store-column-not-fou import { DataStoreNameConflictError } from './errors/data-store-name-conflict.error'; import { DataStoreNotFoundError } from './errors/data-store-not-found.error'; import { DataStoreValidationError } from './errors/data-store-validation.error'; -import { toTableName, normalizeRows } from './utils/sql-utils'; +import { normalizeRows } from './utils/sql-utils'; @Service() export class DataStoreService { @@ -112,10 +112,7 @@ export class DataStoreService { // a renamed/removed column appearing here (or added column missing) if the store was // modified between when the frontend sent the request and we received it const columns = await this.dataStoreColumnRepository.getColumns(dataStoreId); - const result = await this.dataStoreRowsRepository.getManyAndCount( - toTableName(dataStoreId), - dto, - ); + const result = await this.dataStoreRowsRepository.getManyAndCount(dataStoreId, dto); return { count: result.count, data: normalizeRows(result.data, columns), @@ -133,7 +130,7 @@ export class DataStoreService { await this.validateRows(dataStoreId, rows); const columns = await this.dataStoreColumnRepository.getColumns(dataStoreId); - return await this.dataStoreRowsRepository.insertRows(toTableName(dataStoreId), rows, columns); + return await this.dataStoreRowsRepository.insertRows(dataStoreId, rows, columns); } async upsertRows(dataStoreId: string, projectId: string, dto: UpsertDataStoreRowsDto) { @@ -146,7 +143,7 @@ export class DataStoreService { const columns = await this.dataStoreColumnRepository.getColumns(dataStoreId); - return await this.dataStoreRowsRepository.upsertRows(toTableName(dataStoreId), dto, columns); + return await this.dataStoreRowsRepository.upsertRows(dataStoreId, dto, columns); } async updateRow(dataStoreId: string, projectId: string, dto: UpdateDataStoreRowDto) { @@ -170,14 +167,14 @@ export class DataStoreService { this.validateRowsWithColumns([filter], columns, true, true); this.validateRowsWithColumns([data], columns, true, false); - await this.dataStoreRowsRepository.updateRow(toTableName(dataStoreId), data, filter, columns); + await this.dataStoreRowsRepository.updateRow(dataStoreId, data, filter, columns); return true; } async deleteRows(dataStoreId: string, projectId: string, ids: number[]) { await this.validateDataStoreExists(dataStoreId, projectId); - return await this.dataStoreRowsRepository.deleteRows(toTableName(dataStoreId), ids); + return await this.dataStoreRowsRepository.deleteRows(dataStoreId, ids); } private validateRowsWithColumns( diff --git a/packages/cli/src/modules/data-store/data-store.types.ts b/packages/cli/src/modules/data-store/data-store.types.ts index a648000a815..fe933030d79 100644 --- a/packages/cli/src/modules/data-store/data-store.types.ts +++ b/packages/cli/src/modules/data-store/data-store.types.ts @@ -1 +1 @@ -export type DataStoreUserTableName = `data_store_user_${string}`; +export type DataStoreUserTableName = `${string}data_store_user_${string}`; diff --git a/packages/cli/src/modules/data-store/utils/sql-utils.ts b/packages/cli/src/modules/data-store/utils/sql-utils.ts index 5bb82811132..d8dbb6113c8 100644 --- a/packages/cli/src/modules/data-store/utils/sql-utils.ts +++ b/packages/cli/src/modules/data-store/utils/sql-utils.ts @@ -8,10 +8,10 @@ import type { DataSourceOptions } from '@n8n/typeorm'; import type { DataStoreColumnJsType, DataStoreRows } from 'n8n-workflow'; import { UnexpectedError } from 'n8n-workflow'; -import { NotFoundError } from '@/errors/response-errors/not-found.error'; - import type { DataStoreUserTableName } from '../data-store.types'; +import { NotFoundError } from '@/errors/response-errors/not-found.error'; + export function toDslColumns(columns: DataStoreCreateColumnSchema[]): DslColumn[] { return columns.map((col) => { const name = new DslColumn(col.name.trim()); @@ -137,10 +137,6 @@ export function quoteIdentifier(name: string, dbType: DataSourceOptions['type']) } } -export function toTableName(dataStoreId: string): DataStoreUserTableName { - return `data_store_user_${dataStoreId}`; -} - type WithInsertId = { insertId: number }; type WithRowId = { id: number }; diff --git a/packages/cli/test/integration/shared/db/data-stores.ts b/packages/cli/test/integration/shared/db/data-stores.ts index cb03ef32814..ceb2fc041db 100644 --- a/packages/cli/test/integration/shared/db/data-stores.ts +++ b/packages/cli/test/integration/shared/db/data-stores.ts @@ -7,7 +7,6 @@ import type { DataStoreRows } from 'n8n-workflow'; import { DataStoreColumnRepository } from '@/modules/data-store/data-store-column.repository'; import { DataStoreRowsRepository } from '@/modules/data-store/data-store-rows.repository'; import { DataStoreRepository } from '@/modules/data-store/data-store.repository'; -import { toTableName } from '@/modules/data-store/utils/sql-utils'; export const createDataStore = async ( project: Project, @@ -37,7 +36,7 @@ export const createDataStore = async ( const columns = await dataStoreColumnRepository.getColumns(dataStore.id); const dataStoreRowsRepository = Container.get(DataStoreRowsRepository); - await dataStoreRowsRepository.insertRows(toTableName(dataStore.id), options.data, columns); + await dataStoreRowsRepository.insertRows(dataStore.id, options.data, columns); } return dataStore;