From 12aa77746e47c8fd9dee3af66f1fcc122769e978 Mon Sep 17 00:00:00 2001 From: Charlie Kolb Date: Thu, 4 Sep 2025 16:18:50 +0200 Subject: [PATCH] fix(Data Table Node): Add bulk insert option (no-changelog) (#19176) --- .../nodes/DataTable/actions/router.ts | 72 +++++++++++++------ .../DataTable/actions/row/insert.operation.ts | 11 +++ 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/packages/nodes-base/nodes/DataTable/actions/router.ts b/packages/nodes-base/nodes/DataTable/actions/router.ts index 490b2a0707..0a0149996a 100644 --- a/packages/nodes-base/nodes/DataTable/actions/router.ts +++ b/packages/nodes-base/nodes/DataTable/actions/router.ts @@ -1,14 +1,34 @@ -import type { IExecuteFunctions, INodeExecutionData, AllEntities } from 'n8n-workflow'; -import { NodeOperationError } from 'n8n-workflow'; +import type { + IExecuteFunctions, + INodeExecutionData, + AllEntities, + NodeOperationError, +} from 'n8n-workflow'; import * as row from './row/Row.resource'; +import { DATA_TABLE_ID_FIELD } from '../common/fields'; +import { getDataTableProxyExecute } from '../common/utils'; type DataTableNodeType = AllEntities<{ row: 'insert' | 'get' | 'deleteRows' | 'update' | 'upsert'; }>; +const BULK_OPERATIONS = ['insert'] as const; + +function canBulk(operation: string): operation is (typeof BULK_OPERATIONS)[number] { + return (BULK_OPERATIONS as readonly string[]).includes(operation); +} + +function hasComplexId(ctx: IExecuteFunctions) { + const dataStoreIdExpr = ctx.getNodeParameter(`${DATA_TABLE_ID_FIELD}.value`, 0, undefined, { + rawExpressions: true, + }); + + return typeof dataStoreIdExpr === 'string' && dataStoreIdExpr.includes('{'); +} + export async function router(this: IExecuteFunctions): Promise { - const operationResult: INodeExecutionData[] = []; + let operationResult: INodeExecutionData[] = []; let responseData: INodeExecutionData[] = []; const items = this.getInputData(); @@ -20,34 +40,44 @@ export async function router(this: IExecuteFunctions): Promise ({ + json, error: error as NodeOperationError, - }); + })); } else { throw error; } } + } else { + for (let i = 0; i < items.length; i++) { + try { + responseData = await row[dataTableNodeData.operation].execute.call(this, i); + const executionData = this.helpers.constructExecutionMetaData(responseData, { + itemData: { item: i }, + }); + + operationResult.push.apply(operationResult, executionData); + } catch (error) { + if (this.continueOnFail()) { + operationResult.push({ + json: this.getInputData(i)[0].json, + error: error as NodeOperationError, + }); + } else { + throw error; + } + } + } } return [operationResult]; diff --git a/packages/nodes-base/nodes/DataTable/actions/row/insert.operation.ts b/packages/nodes-base/nodes/DataTable/actions/row/insert.operation.ts index 83b2be2c26..21157064fb 100644 --- a/packages/nodes-base/nodes/DataTable/actions/row/insert.operation.ts +++ b/packages/nodes-base/nodes/DataTable/actions/row/insert.operation.ts @@ -1,4 +1,5 @@ import type { + IDataStoreProjectService, IDisplayOptions, IExecuteFunctions, INodeExecutionData, @@ -30,3 +31,13 @@ export async function execute( const insertedRows = await dataStoreProxy.insertRows([row]); return insertedRows.map((json) => ({ json })); } + +export async function executeBulk( + this: IExecuteFunctions, + proxy: IDataStoreProjectService, +): Promise { + const rows = this.getInputData().flatMap((_, i) => [getAddRow(this, i)]); + + const insertedRows = await proxy.insertRows(rows); + return insertedRows.map((json, item) => ({ json, pairedItem: { item } })); +}