diff --git a/extensions/ql-vscode/src/legacy-query-server/run-queries.ts b/extensions/ql-vscode/src/legacy-query-server/run-queries.ts index c63ab7f90..66b452911 100644 --- a/extensions/ql-vscode/src/legacy-query-server/run-queries.ts +++ b/extensions/ql-vscode/src/legacy-query-server/run-queries.ts @@ -27,7 +27,7 @@ import { redactableError } from "../pure/errors"; import { CoreQueryResults, CoreQueryTarget } from "../queryRunner"; import { Position } from "../pure/messages-shared"; -async function compileQuery( +export async function compileQuery( qs: qsClient.QueryServerClient, program: messages.QlProgram, quickEvalPosition: Position | undefined, @@ -185,7 +185,7 @@ export class QueryInProgress { readonly querySaveDir: string, readonly dbItemPath: string, databaseHasMetadataFile: boolean, - readonly queryDbscheme: string, // the dbscheme file the query expects, based on library path resolution + readonly queryDbscheme: string, // the dbscheme file the query expects, ba`sed on library path resolution readonly quickEvalPosition?: messages.Position, readonly metadata?: QueryMetadata, readonly templates?: Record, diff --git a/extensions/ql-vscode/src/local-queries.ts b/extensions/ql-vscode/src/local-queries.ts index 4c7fea47d..1378e0092 100644 --- a/extensions/ql-vscode/src/local-queries.ts +++ b/extensions/ql-vscode/src/local-queries.ts @@ -22,7 +22,11 @@ import { tryGetQueryMetadata, } from "./helpers"; import { displayQuickQuery } from "./quick-query"; -import { CoreQueryResults, QueryRunner } from "./queryRunner"; +import { + CoreCompletedQuery, + CoreQueryResults, + QueryRunner, +} from "./queryRunner"; import { QueryHistoryManager } from "./query-history/query-history-manager"; import { DatabaseUI } from "./local-databases-ui"; import { ResultsView } from "./interface"; @@ -406,64 +410,82 @@ export class LocalQueries extends DisposableObject { databaseItem: DatabaseItem | undefined, range?: Range, ): Promise { - if (this.queryRunner !== undefined) { - const selectedQuery = await determineSelectedQuery( - queryUri, - quickEval, - range, + await this.compileAndRunQueryInternal( + quickEval, + queryUri, + progress, + token, + databaseItem, + range, + ); + } + + /** Used by tests */ + public async compileAndRunQueryInternal( + quickEval: boolean, + queryUri: Uri | undefined, + progress: ProgressCallback, + token: CancellationToken, + databaseItem: DatabaseItem | undefined, + range?: Range, + ): Promise { + const selectedQuery = await determineSelectedQuery( + queryUri, + quickEval, + range, + ); + + // If no databaseItem is specified, use the database currently selected in the Databases UI + databaseItem = + databaseItem || (await this.databaseUI.getDatabaseItem(progress, token)); + if (databaseItem === undefined) { + throw new Error("Can't run query without a selected database"); + } + + const coreQueryRun = this.queryRunner.createQueryRun( + databaseItem.databaseUri.fsPath, + { + queryPath: selectedQuery.queryPath, + quickEvalPosition: selectedQuery.quickEvalPosition, + }, + true, + getOnDiskWorkspaceFolders(), + this.queryStorageDir, + undefined, + undefined, + ); + + // handle cancellation from the history view. + const source = new CancellationTokenSource(); + try { + token.onCancellationRequested(() => source.cancel()); + + const localQueryRun = await this.createLocalQueryRun( + selectedQuery, + databaseItem, + coreQueryRun.outputDir, + source, ); - // If no databaseItem is specified, use the database currently selected in the Databases UI - databaseItem = - databaseItem || - (await this.databaseUI.getDatabaseItem(progress, token)); - if (databaseItem === undefined) { - throw new Error("Can't run query without a selected database"); - } - - const coreQueryRun = this.queryRunner.createQueryRun( - databaseItem.databaseUri.fsPath, - { - queryPath: selectedQuery.queryPath, - quickEvalPosition: selectedQuery.quickEvalPosition, - }, - true, - getOnDiskWorkspaceFolders(), - this.queryStorageDir, - undefined, - undefined, - ); - - // handle cancellation from the history view. - const source = new CancellationTokenSource(); try { - token.onCancellationRequested(() => source.cancel()); - - const localQueryRun = await this.createLocalQueryRun( - selectedQuery, - databaseItem, - coreQueryRun.outputDir, - source, + const results = await coreQueryRun.evaluate( + progress, + source.token, + localQueryRun.logger, ); - try { - const results = await coreQueryRun.evaluate( - progress, - source.token, - localQueryRun.logger, - ); + await localQueryRun.complete(results); - await localQueryRun.complete(results); - } catch (e) { - const err = asError(e); - err.message = `Error running query: ${err.message}`; - localQueryRun.queryInfo.failureReason = err.message; - throw e; - } - } finally { - await this.queryHistoryManager.refreshTreeView(); - source.dispose(); + return results; + } catch (e) { + const err = asError(e); + err.message = `Error running query: ${err.message}`; + localQueryRun.queryInfo.failureReason = err.message; + throw e; } + } finally { + await this.queryHistoryManager.refreshTreeView(); + source.dispose(); } } diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/queries.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/queries.test.ts index c708c0bd8..817dd0797 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/queries.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/queries.test.ts @@ -19,13 +19,11 @@ import { import { importArchiveDatabase } from "../../../src/databaseFetcher"; import { CliVersionConstraint, CodeQLCliServer } from "../../../src/cli"; import { describeWithCodeQL } from "../cli"; -import { tmpDir } from "../../../src/helpers"; -import { createInitialQueryInfo } from "../../../src/run-queries-shared"; import { QueryRunner } from "../../../src/queryRunner"; -import { CompletedQueryInfo } from "../../../src/query-results"; import { SELECT_QUERY_NAME } from "../../../src/contextual/locationFinder"; import { createMockCommandManager } from "../../__mocks__/commandsMock"; import { LocalQueries } from "../../../src/local-queries"; +import { QueryResultType } from "../../../src/pure/new-messages"; jest.setTimeout(20_000); @@ -134,22 +132,21 @@ describeWithCodeQL()("Queries", () => { } async function runQueryWithExtensions() { - const result = new CompletedQueryInfo( - await localQueries.compileAndRunQueryAgainstDatabase( - dbItem, - await mockInitialQueryInfo(queryUsingExtensionPath), - join(tmpDir.name, "mock-storage-path"), - progress, - token, - ), + const result = await localQueries.compileAndRunQueryInternal( + false, + Uri.file(queryUsingExtensionPath), + progress, + token, + dbItem, + undefined, ); // Check that query was successful - expect(result.successful).toBe(true); + expect(result.resultType).toBe(QueryResultType.SUCCESS); // Load query results const chunk = await qs.cliServer.bqrsDecode( - result.getResultsPath(SELECT_QUERY_NAME, true), + result.outputDir.bqrsPath, SELECT_QUERY_NAME, { // there should only be one result @@ -165,31 +162,33 @@ describeWithCodeQL()("Queries", () => { it("should run a query", async () => { const queryPath = join(__dirname, "data", "simple-query.ql"); - const result = localQueries.compileAndRunQueryAgainstDatabase( - dbItem, - await mockInitialQueryInfo(queryPath), - join(tmpDir.name, "mock-storage-path"), + const result = await localQueries.compileAndRunQueryInternal( + false, + Uri.file(queryPath), progress, token, + dbItem, + undefined, ); // just check that the query was successful - expect((await result).successful).toBe(true); + expect(result.resultType).toBe(QueryResultType.SUCCESS); }); // Asserts a fix for bug https://github.com/github/vscode-codeql/issues/733 it("should restart the database and run a query", async () => { await commands.executeCommand("codeQL.restartQueryServer"); const queryPath = join(__dirname, "data", "simple-query.ql"); - const result = await localQueries.compileAndRunQueryAgainstDatabase( - dbItem, - await mockInitialQueryInfo(queryPath), - join(tmpDir.name, "mock-storage-path"), + const result = await localQueries.compileAndRunQueryInternal( + false, + Uri.file(queryPath), progress, token, + dbItem, + undefined, ); - expect(result.successful).toBe(true); + expect(result.resultType).toBe(QueryResultType.SUCCESS); }); it("should create a quick query", async () => { @@ -239,15 +238,4 @@ describeWithCodeQL()("Queries", () => { // ignore } } - - async function mockInitialQueryInfo(queryPath: string) { - return await createInitialQueryInfo( - Uri.file(queryPath), - { - name: dbItem.name, - databaseUri: dbItem.databaseUri.toString(), - }, - false, - ); - } }); diff --git a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-databases.test.ts b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-databases.test.ts index c106110e8..b83573faf 100644 --- a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-databases.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-databases.test.ts @@ -5,6 +5,7 @@ import { CancellationToken, ExtensionContext, Uri, workspace } from "vscode"; import { DatabaseContents, + DatabaseContentsWithDbScheme, DatabaseEventKind, DatabaseItemImpl, DatabaseManager, @@ -687,7 +688,7 @@ describe("local databases", () => { resolveDatabaseContentsSpy = jest .spyOn(DatabaseResolver, "resolveDatabaseContents") - .mockResolvedValue({} as DatabaseContents); + .mockResolvedValue({} as DatabaseContentsWithDbScheme); addDatabaseSourceArchiveFolderSpy = jest.spyOn( databaseManager, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/contextual/astBuilder.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/contextual/astBuilder.test.ts index 02022aeba..0e4f308ee 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/contextual/astBuilder.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/contextual/astBuilder.test.ts @@ -3,7 +3,7 @@ import { readFileSync } from "fs-extra"; import AstBuilder from "../../../../src/contextual/astBuilder"; import { CodeQLCliServer } from "../../../../src/cli"; import { Uri } from "vscode"; -import { QueryWithResults } from "../../../../src/run-queries-shared"; +import { QueryOutputDir } from "../../../../src/run-queries-shared"; import { mockDatabaseItem, mockedObject } from "../../utils/mocking.helpers"; /** @@ -137,13 +137,7 @@ describe("AstBuilder", () => { function createAstBuilder() { return new AstBuilder( - { - query: { - resultsPaths: { - resultsPath: "/a/b/c", - }, - }, - } as QueryWithResults, + new QueryOutputDir("/a/b/c"), mockCli, mockDatabaseItem({ resolveSourceFile: undefined, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/run-queries.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/run-queries.test.ts index d576b1004..37067b2bc 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/run-queries.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/run-queries.test.ts @@ -13,7 +13,10 @@ import { tmpDir } from "../../../src/helpers"; import { QueryServerClient } from "../../../src/legacy-query-server/queryserver-client"; import { CodeQLCliServer } from "../../../src/cli"; import { SELECT_QUERY_NAME } from "../../../src/contextual/locationFinder"; -import { QueryInProgress } from "../../../src/legacy-query-server/run-queries"; +import { + QueryInProgress, + compileQuery as compileQueryLegacy, +} from "../../../src/legacy-query-server/run-queries"; import { LegacyQueryRunner } from "../../../src/legacy-query-server/legacyRunner"; import { DatabaseItem } from "../../../src/local-databases"; import { DeepPartial, mockedObject } from "../utils/mocking.helpers"; @@ -30,7 +33,6 @@ describe("run-queries", () => { const saveDir = "query-save-dir"; const info = createMockQueryInfo(true, saveDir); - expect(info.compiledQueryPath).toBe(join(saveDir, "compiledQuery.qlo")); expect(info.queryEvalInfo.dilPath).toBe(join(saveDir, "results.dil")); expect(info.queryEvalInfo.resultsPaths.resultsPath).toBe( join(saveDir, "results.bqrs"), @@ -185,14 +187,15 @@ describe("run-queries", () => { queryPath: "", }; - const results = await info.compile( + const results = await compileQueryLegacy( qs as any, mockQlProgram, + undefined, + info.queryEvalInfo, mockProgress as any, mockCancel as any, qs.logger, ); - expect(results).toEqual([{ message: "err", severity: Severity.ERROR }]); expect(qs.sendRequest).toHaveBeenCalledTimes(1); @@ -214,7 +217,7 @@ describe("run-queries", () => { timeoutSecs: 5, }, queryToCheck: mockQlProgram, - resultPath: info.compiledQueryPath, + resultPath: info.queryEvalInfo.compileQueryPath, target: { query: {} }, }, mockCancel,