mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-13 16:14:07 -08:00
fix(Microsoft SQL Node): Fix execute query to allow for non select query to run (#11335)
This commit is contained in:
parent
901888d5b1
commit
ba158b4f85
|
@ -1,9 +1,12 @@
|
|||
import type { IResult } from 'mssql';
|
||||
import mssql from 'mssql';
|
||||
import type { IDataObject, INodeExecutionData } from 'n8n-workflow';
|
||||
import { deepCopy } from 'n8n-workflow';
|
||||
import mssql from 'mssql';
|
||||
import type { ITables, OperationInputData } from './interfaces';
|
||||
|
||||
import { chunk, flatten } from '@utils/utilities';
|
||||
|
||||
import type { ITables, OperationInputData } from './interfaces';
|
||||
|
||||
/**
|
||||
* Returns a copy of the item which only contains the json data and
|
||||
* of that only the defined properties
|
||||
|
@ -234,3 +237,36 @@ export async function deleteOperation(tables: ITables, pool: mssql.ConnectionPoo
|
|||
0,
|
||||
);
|
||||
}
|
||||
|
||||
export async function executeSqlQueryAndPrepareResults(
|
||||
pool: mssql.ConnectionPool,
|
||||
rawQuery: string,
|
||||
itemIndex: number,
|
||||
): Promise<INodeExecutionData[]> {
|
||||
const rawResult: IResult<any> = await pool.request().query(rawQuery);
|
||||
const { recordsets, rowsAffected } = rawResult;
|
||||
if (Array.isArray(recordsets) && recordsets.length > 0) {
|
||||
const result: IDataObject[] = recordsets.length > 1 ? flatten(recordsets) : recordsets[0];
|
||||
|
||||
return result.map((entry) => ({
|
||||
json: entry,
|
||||
pairedItem: [{ item: itemIndex }],
|
||||
}));
|
||||
} else if (rowsAffected && rowsAffected.length > 0) {
|
||||
// Handle non-SELECT queries (e.g., INSERT, UPDATE, DELETE)
|
||||
return rowsAffected.map((affectedRows, idx) => ({
|
||||
json: {
|
||||
message: `Query ${idx + 1} executed successfully`,
|
||||
rowsAffected: affectedRows,
|
||||
},
|
||||
pairedItem: [{ item: itemIndex }],
|
||||
}));
|
||||
} else {
|
||||
return [
|
||||
{
|
||||
json: { message: 'Query executed successfully, but no rows were affected' },
|
||||
pairedItem: [{ item: itemIndex }],
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,17 +12,17 @@ import {
|
|||
NodeConnectionType,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import type { ITables } from './interfaces';
|
||||
import { flatten, generatePairedItemData, getResolvables } from '@utils/utilities';
|
||||
|
||||
import {
|
||||
configurePool,
|
||||
createTableStruct,
|
||||
deleteOperation,
|
||||
executeSqlQueryAndPrepareResults,
|
||||
insertOperation,
|
||||
updateOperation,
|
||||
} from './GenericFunctions';
|
||||
|
||||
import { flatten, generatePairedItemData, getResolvables } from '@utils/utilities';
|
||||
import type { ITables } from './interfaces';
|
||||
|
||||
export class MicrosoftSql implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
|
@ -268,17 +268,8 @@ export class MicrosoftSql implements INodeType {
|
|||
this.evaluateExpression(resolvable, i) as string,
|
||||
);
|
||||
}
|
||||
|
||||
const { recordsets }: IResult<any[]> = await pool.request().query(rawQuery);
|
||||
|
||||
const result: IDataObject[] = recordsets.length > 1 ? flatten(recordsets) : recordsets[0];
|
||||
|
||||
for (const entry of result) {
|
||||
returnData.push({
|
||||
json: entry,
|
||||
pairedItem: [{ item: i }],
|
||||
});
|
||||
}
|
||||
const results = await executeSqlQueryAndPrepareResults(pool, rawQuery, i);
|
||||
returnData.push(...results);
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
import { Request } from 'mssql';
|
||||
import type { IResult } from 'mssql';
|
||||
import type mssql from 'mssql';
|
||||
import type { IDataObject } from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
configurePool,
|
||||
deleteOperation,
|
||||
executeSqlQueryAndPrepareResults,
|
||||
insertOperation,
|
||||
mssqlChunk,
|
||||
updateOperation,
|
||||
} from '../GenericFunctions';
|
||||
|
||||
describe('MSSQL tests', () => {
|
||||
let querySpy: jest.SpyInstance<void, Parameters<Request['query']>>;
|
||||
let querySpy: jest.SpyInstance;
|
||||
let request: Request;
|
||||
|
||||
const assertParameters = (parameters: unknown[][] | IDataObject) => {
|
||||
|
@ -34,18 +38,12 @@ describe('MSSQL tests', () => {
|
|||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
request = this;
|
||||
return [
|
||||
[
|
||||
[
|
||||
{
|
||||
recordsets: [],
|
||||
recordset: undefined,
|
||||
output: {},
|
||||
rowsAffected: [0],
|
||||
},
|
||||
],
|
||||
],
|
||||
];
|
||||
return {
|
||||
recordsets: [],
|
||||
recordset: [],
|
||||
output: {},
|
||||
rowsAffected: [0],
|
||||
} as unknown as IResult<unknown>;
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -154,4 +152,94 @@ describe('MSSQL tests', () => {
|
|||
expect(chunks.map((chunk) => chunk.length)).toEqual([699, 699, 699, 699, 204]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('executeSqlQueryAndPrepareResults', () => {
|
||||
it('should handle SELECT query with single record', async () => {
|
||||
querySpy.mockResolvedValueOnce({
|
||||
recordsets: [[{ id: 1, name: 'Test' }]] as any,
|
||||
recordset: [{ id: 1, name: 'Test', columns: [{ name: 'id' }, { name: 'name' }] }],
|
||||
rowsAffected: [1],
|
||||
output: {},
|
||||
} as unknown as IResult<unknown>);
|
||||
|
||||
const pool = { request: () => new Request() } as any as mssql.ConnectionPool;
|
||||
const result = await executeSqlQueryAndPrepareResults(pool, 'SELECT * FROM users', 0);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
json: { id: 1, name: 'Test' },
|
||||
pairedItem: [{ item: 0 }],
|
||||
},
|
||||
]);
|
||||
expect(querySpy).toHaveBeenCalledWith('SELECT * FROM users');
|
||||
});
|
||||
|
||||
it('should handle SELECT query with multiple records', async () => {
|
||||
querySpy.mockResolvedValueOnce({
|
||||
recordsets: [[{ id: 1 }], [{ name: 'Test' }]] as unknown,
|
||||
rowsAffected: [1, 1],
|
||||
output: {},
|
||||
} as unknown as IResult<unknown>);
|
||||
|
||||
const pool = { request: () => new Request() } as any as mssql.ConnectionPool;
|
||||
const result = await executeSqlQueryAndPrepareResults(pool, 'SELECT id; SELECT name', 1);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ json: { id: 1 }, pairedItem: [{ item: 1 }] },
|
||||
{ json: { name: 'Test' }, pairedItem: [{ item: 1 }] },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle non-SELECT query', async () => {
|
||||
querySpy.mockResolvedValueOnce({
|
||||
recordsets: [],
|
||||
recordset: [],
|
||||
rowsAffected: [5],
|
||||
output: {},
|
||||
} as unknown as IResult<unknown>);
|
||||
|
||||
const pool = { request: () => new Request() } as any as mssql.ConnectionPool;
|
||||
const result = await executeSqlQueryAndPrepareResults(pool, 'UPDATE users SET active = 1', 2);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
json: { message: 'Query 1 executed successfully', rowsAffected: 5 },
|
||||
pairedItem: [{ item: 2 }],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle query with no affected rows', async () => {
|
||||
querySpy.mockResolvedValueOnce({
|
||||
recordsets: [],
|
||||
recordset: [],
|
||||
rowsAffected: [],
|
||||
output: {},
|
||||
} as unknown as IResult<unknown>);
|
||||
|
||||
const pool = { request: () => new Request() } as any as mssql.ConnectionPool;
|
||||
const result = await executeSqlQueryAndPrepareResults(
|
||||
pool,
|
||||
'DELETE FROM users WHERE id = 999',
|
||||
3,
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
json: { message: 'Query executed successfully, but no rows were affected' },
|
||||
pairedItem: [{ item: 3 }],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should throw an error when query fails', async () => {
|
||||
const errorMessage = 'Database error';
|
||||
querySpy.mockRejectedValueOnce(new Error(errorMessage));
|
||||
|
||||
const pool = { request: () => new Request() } as any as mssql.ConnectionPool;
|
||||
await expect(executeSqlQueryAndPrepareResults(pool, 'INVALID SQL', 4)).rejects.toThrow(
|
||||
errorMessage,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue