diff --git a/src/commands/serverCommand.ts b/src/commands/serverCommand.ts index 95becbdc..f361c629 100644 --- a/src/commands/serverCommand.ts +++ b/src/commands/serverCommand.ts @@ -812,7 +812,7 @@ export async function executeQuery( query = sanitizeQuery(query); if (ext.resultsViewProvider.isVisible()) { - const queryRes = await ext.connection.executeQuery(query, context, false); + const queryRes = await ext.connection.executeQuery(query, context, true); writeQueryResultsToView(queryRes, query); } else { const queryRes = await ext.connection.executeQuery(query, context, true); diff --git a/src/models/connection.ts b/src/models/connection.ts index 6766cb60..310741e5 100644 --- a/src/models/connection.ts +++ b/src/models/connection.ts @@ -15,10 +15,7 @@ import * as nodeq from "node-q"; import { commands, window } from "vscode"; import { ext } from "../extensionVariables"; import { delay } from "../utils/core"; -import { - convertArrayOfArraysToObjects, - handleQueryResults, -} from "../utils/execution"; +import { convertStringToArray, handleQueryResults } from "../utils/execution"; import { queryWrapper } from "../utils/queryUtils"; import { QueryResult, QueryResultType } from "./queryResult"; @@ -26,6 +23,7 @@ export class Connection { private options: nodeq.ConnectionParameters; private connection?: nodeq.Connection; public connected: boolean; + private result?: string; constructor(connectionString: string, creds?: string[], tls?: boolean) { const params = connectionString.split(":"); @@ -98,16 +96,11 @@ export class Connection { context?: string, stringify?: boolean ): Promise { - let result; - let retryCount = 0; - while (this.connection === undefined) { - if (retryCount > ext.maxRetryCount) { - return "timeout"; - } - await delay(500); - retryCount++; - } + await this.waitForConnection(); + if (!this.connection) { + return "timeout"; + } const wrapper = queryWrapper(); this.connection.k( wrapper, @@ -116,29 +109,53 @@ export class Connection { !!stringify, (err: Error, res: QueryResult) => { if (err) { - result = handleQueryResults(err.toString(), QueryResultType.Error); - } else if (res) { - if (res.errored) { - result = handleQueryResults( - res.error + (res.backtrace ? "\n" + res.backtrace : ""), - QueryResultType.Error - ); - } else { - result = res.result; - } + this.result = handleQueryResults( + err.toString(), + QueryResultType.Error + ); + } + if (res) { + this.handleQueryResult(res); } } ); - while (result === undefined || result === null) { + const result = await this.waitForResult(); + + if (ext.resultsViewProvider.isVisible()) { + return convertStringToArray(result); + } + + return result; + } + + private async waitForConnection(): Promise { + let retryCount = 0; + while (this.connection === undefined) { + if (retryCount > ext.maxRetryCount) { + throw new Error("timeout"); + } await delay(500); + retryCount++; } + } - if (!stringify) { - result = convertArrayOfArraysToObjects(result); + private handleQueryResult = (res: QueryResult): void => { + if (res.errored) { + this.result = handleQueryResults( + res.error + (res.backtrace ? "\n" + res.backtrace : ""), + QueryResultType.Error + ); + } else { + this.result = res.result; } + }; - return result; + private async waitForResult(): Promise { + while (this.result === undefined || this.result === null) { + await delay(500); + } + return this.result; } public async executeQueryRaw(command: string): Promise { diff --git a/src/utils/execution.ts b/src/utils/execution.ts index 5ff22de7..2b945f92 100644 --- a/src/utils/execution.ts +++ b/src/utils/execution.ts @@ -156,3 +156,69 @@ export function convertArrayOfArraysToObjects(arr: any): any[] { return result; } + +function processLineWithSeparator(line: string, index: number): object { + const parts = line.split("|").map((part) => part.trim()); + return { Index: index + 1, Key: parts[0], Value: parts[1] }; +} + +function processLineWithoutSeparator(line: string, index: number): object[] { + if (!line.startsWith('"') && line.includes(" ")) { + // Split the line into parts by spaces and map each part to an object + return line.split(" ").map((part, i) => { + return { Index: index + i + 1, Value: part }; + }); + } else { + return [{ Index: index + 1, Value: line.trim() }]; + } +} + +function processLine( + line: string, + index: number, + fieldLengths: number[], + fieldNames: string[] +): object { + let start = 0; + const obj: { [key: string]: any } = { Index: index + 1 }; + fieldLengths.forEach((length, i) => { + obj[fieldNames[i]] = line.substring(start, start + length).trim(); + start += length; + }); + return obj; +} + +export function convertStringToArray(str: string): any[] { + const lines = str.split("\n").filter((line) => line.trim() !== ""); + if (lines.length > 2 && lines[1].startsWith("---")) { + const fieldNames = lines[0].split(" ").filter((part) => part !== ""); + lines.splice(1, 1); + let total = 0; + const fieldLengths = fieldNames.map((name, i) => { + if (i === fieldNames.length - 1) { + return lines[0].length - total; + } + const elementLength = + lines[0].indexOf(fieldNames[i + 1] ?? "") - lines[0].indexOf(name); + total += elementLength; + return elementLength; + }); + lines.shift(); + return lines.flatMap((line, index) => + fieldLengths.length > 0 + ? processLine(line, index, fieldLengths, fieldNames) + : [] + ); + } + + return lines + .flatMap((line, index) => { + const parts = line.split("|").map((part) => part.trim()); + return parts.length === 2 + ? processLineWithSeparator(line, index) + : processLineWithoutSeparator(line, index); + }) + .filter( + (obj) => !("Value" in obj && (obj.Value as string).startsWith("-")) + ); +} diff --git a/test/suite/utils.test.ts b/test/suite/utils.test.ts index 33914ea8..f242ae4a 100644 --- a/test/suite/utils.test.ts +++ b/test/suite/utils.test.ts @@ -316,6 +316,69 @@ describe("Utils", () => { assert.deepStrictEqual(result, expectedOutput); }); }); + + describe("convertArrayOfObjectsToArrays", () => { + it("convertStringToArray handles string with separator", () => { + const input = "key1 | value1\nkey2 | value2"; + const expectedOutput = [ + { Index: 1, Key: "key1", Value: "value1" }, + { Index: 2, Key: "key2", Value: "value2" }, + ]; + const output = executionUtils.convertStringToArray(input); + assert.deepStrictEqual(output, expectedOutput); + }); + + it("convertStringToArray handles string without separator", () => { + const input = "value1\nvalue2"; + const expectedOutput = [ + { Index: 1, Value: "value1" }, + { Index: 2, Value: "value2" }, + ]; + const output = executionUtils.convertStringToArray(input); + assert.deepStrictEqual(output, expectedOutput); + }); + + it("convertStringToArray handles string with field names and lengths", () => { + const input = "name age\n---\nJohn 25\nDoe 30"; + const expectedOutput = [ + { Index: 1, name: "John", age: "25" }, + { Index: 2, name: "Doe", age: "30" }, + ]; + const output = executionUtils.convertStringToArray(input); + assert.deepStrictEqual(output, expectedOutput); + }); + + it("convertStringToArray filters out lines starting with '-'", () => { + const input = "key1 | value1\nkey2 | value2\nkey3 | value3"; + const expectedOutput = [ + { Index: 1, Key: "key1", Value: "value1" }, + { Index: 2, Key: "key2", Value: "value2" }, + { Index: 3, Key: "key3", Value: "value3" }, + ]; + const output = executionUtils.convertStringToArray(input); + assert.deepStrictEqual(output, expectedOutput); + }); + + it("convertStringToArray handles single value results", () => { + const input = "2001.01.01D12:00:00.000000000\n"; + const expectedOutput = [ + { Index: 1, Value: "2001.01.01D12:00:00.000000000" }, + ]; + const output = executionUtils.convertStringToArray(input); + assert.deepStrictEqual(output, expectedOutput); + }); + + it("convertStringToArray handles single line with multiple value results", () => { + const input = + "2001.01.01D12:00:00.000000000 2001.01.01D12:00:00.000000001\n"; + const expectedOutput = [ + { Index: 1, Value: "2001.01.01D12:00:00.000000000" }, + { Index: 2, Value: "2001.01.01D12:00:00.000000001" }, + ]; + const output = executionUtils.convertStringToArray(input); + assert.deepStrictEqual(output, expectedOutput); + }); + }); }); describe("executionConsole", () => {