diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 307fae5396e..57b9e10b253 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -4,6 +4,7 @@ - Rename command "CodeQL: Trim Overlay Base Cache" to "CodeQL: Trim Cache to Overlay-Base" for consistency with "CodeQL: Warm Overlay-Base Cache for [...]" commands. [#4204](https://github.com/github/vscode-codeql/pull/4204) - Deprecate the setting (`codeQL.runningQueries.saveCache`) that aggressively saved intermediate results to the disk cache. [#4210](https://github.com/github/vscode-codeql/pull/4210) +- The CodeQL CLI's `bqrs diff` command is now used in the "Compare Results" view. This makes the view faster, more accurate, and fixes a bug where it would error when comparing a large amount of results. [#4194](https://github.com/github/vscode-codeql/pull/4194) & [#4211](https://github.com/github/vscode-codeql/pull/4211) ## 1.17.6 - 24 October 2025 diff --git a/extensions/ql-vscode/src/codeql-cli/cli-version.ts b/extensions/ql-vscode/src/codeql-cli/cli-version.ts index ebd30cbdb62..15a6616cb1a 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli-version.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli-version.ts @@ -11,6 +11,7 @@ interface VersionResult { export interface CliFeatures { queryServerRunQueries?: boolean; + bqrsDiffResultSets?: boolean; } export interface VersionAndFeatures { diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index 493391d5eb3..e17e1ac198c 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -41,6 +41,12 @@ import { ExitCodeError, getCliError } from "./cli-errors"; import { UserCancellationException } from "../common/vscode/progress"; import type { LanguageClient } from "vscode-languageclient/node"; +/** + * The oldest version of the CLI that we support. This is used to determine + * whether to show a warning about the CLI being too old on startup. + */ +export const OLDEST_SUPPORTED_CLI_VERSION = new SemVer("2.20.7"); + /** * The version of the SARIF format that we are using. */ @@ -205,6 +211,7 @@ interface BqrsDecodeOptions { interface BqrsDiffOptions { retainResultSets?: string[]; + resultSets?: Array<[string, string]>; } type OnLineCallback = (line: string) => Promise; @@ -273,8 +280,6 @@ export class CodeQLCliServer implements Disposable { /** Path to current codeQL executable, or undefined if not running yet. */ codeQlPath: string | undefined; - cliConstraints = new CliVersionConstraint(this); - /** * When set to true, ignore some modal popups and assume user has clicked "yes". */ @@ -1279,6 +1284,12 @@ export class CodeQLCliServer implements Disposable { ...(options?.retainResultSets ? ["--retain-result-sets", options.retainResultSets.join(",")] : []), + ...(options?.resultSets + ? options.resultSets.flatMap(([left, right]) => [ + "--result-sets", + `${left},${right}`, + ]) + : []), bqrsPath1, bqrsPath2, ], @@ -1816,6 +1827,11 @@ export class CodeQLCliServer implements Disposable { public async setUseExtensionPacks(useExtensionPacks: boolean) { await this.cliConfig.setUseExtensionPacks(useExtensionPacks); } + + /** Checks if the CLI supports a specific feature. */ + public async supportsFeature(feature: keyof CliFeatures): Promise { + return (await this.getFeatures())[feature] === true; + } } /** @@ -1922,17 +1938,3 @@ export function shouldDebugQueryServer() { function shouldDebugCliServer() { return isEnvTrue("CLI_SERVER_JAVA_DEBUG"); } - -export class CliVersionConstraint { - // The oldest version of the CLI that we support. This is used to determine - // whether to show a warning about the CLI being too old on startup. - public static OLDEST_SUPPORTED_CLI_VERSION = new SemVer("2.20.7"); - - constructor(private readonly cli: CodeQLCliServer) { - /**/ - } - - async supportsQueryServerRunQueries(): Promise { - return (await this.cli.getFeatures()).queryServerRunQueries === true; - } -} diff --git a/extensions/ql-vscode/src/compare/compare-view.ts b/extensions/ql-vscode/src/compare/compare-view.ts index 32e943f4e26..4ab42794d33 100644 --- a/extensions/ql-vscode/src/compare/compare-view.ts +++ b/extensions/ql-vscode/src/compare/compare-view.ts @@ -19,6 +19,7 @@ import type { CodeQLCliServer } from "../codeql-cli/cli"; import type { DatabaseManager } from "../databases/local-databases"; import { jumpToLocation } from "../databases/local-databases/locations"; import type { BqrsInfo, BqrsResultSetSchema } from "../common/bqrs-cli-types"; +// eslint-disable-next-line import/no-deprecated import resultsDiff from "./resultsDiff"; import type { CompletedLocalQueryInfo } from "../query-results"; import { assertNever, getErrorMessage } from "../common/helpers-pure"; @@ -403,13 +404,18 @@ export class CompareView extends AbstractWebview< toResultSetName, ); - // If the result set names are the same, we use `bqrs diff`. This is more - // efficient, but we can't use it in general as it does not support - // comparing different result sets. - if (fromResultSetName === toResultSetName) { + // We use `bqrs diff` when the `--result-sets` option is supported, or when + // the result set names are the same (in which case we don't need the + // option). + const supportsBqrsDiffResultSets = + await this.cliServer.supportsFeature("bqrsDiffResultSets"); + if (supportsBqrsDiffResultSets || fromResultSetName === toResultSetName) { const { uniquePath1, uniquePath2, cleanup } = await this.cliServer.bqrsDiff(fromPath, toPath, { retainResultSets: [], + resultSets: supportsBqrsDiffResultSets + ? [[fromResultSetName, toResultSetName]] + : [], }); try { const uniqueInfo1 = await this.cliServer.bqrsInfo(uniquePath1); @@ -443,10 +449,13 @@ export class CompareView extends AbstractWebview< await cleanup(); } } else { + // Legacy code path: Perform the diff directly in the extension when we + // can't use `bqrs diff`. const [fromResultSet, toResultSet] = await Promise.all([ this.getResultSet(fromInfo.schemas, fromResultSetName, fromPath), this.getResultSet(toInfo.schemas, toResultSetName, toPath), ]); + // eslint-disable-next-line import/no-deprecated return resultsDiff(fromResultSet, toResultSet); } } diff --git a/extensions/ql-vscode/src/compare/resultsDiff.ts b/extensions/ql-vscode/src/compare/resultsDiff.ts index a0b240a05ac..cf5d2505c51 100644 --- a/extensions/ql-vscode/src/compare/resultsDiff.ts +++ b/extensions/ql-vscode/src/compare/resultsDiff.ts @@ -18,6 +18,10 @@ import type { RawResultSet } from "../common/raw-result-types"; * 1. number of columns do not match * 2. If either query is empty * 3. If the queries are 100% disjoint + * + * @deprecated This function is only used when the `bqrs diff` command does not + * support `--result-sets`. It should be removed when all supported CLI versions + * support this option. */ export default function resultsDiff( fromResults: RawResultSet, diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index c53516185f5..b92cb25a4dd 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -21,7 +21,10 @@ import { activate as archiveFilesystemProvider_activate, zipArchiveScheme, } from "./common/vscode/archive-filesystem-provider"; -import { CliVersionConstraint, CodeQLCliServer } from "./codeql-cli/cli"; +import { + CodeQLCliServer, + OLDEST_SUPPORTED_CLI_VERSION, +} from "./codeql-cli/cli"; import { ADD_DATABASE_SOURCE_TO_WORKSPACE_SETTING, addDatabaseSourceToWorkspace, @@ -451,29 +454,19 @@ export async function activate( let unsupportedWarningShown = false; codeQlExtension.cliServer.addVersionChangedListener((ver) => { - if (!ver) { - return; - } - - if (unsupportedWarningShown) { - return; - } - if ( - CliVersionConstraint.OLDEST_SUPPORTED_CLI_VERSION.compare( - ver.version, - ) <= 0 + ver && + !unsupportedWarningShown && + OLDEST_SUPPORTED_CLI_VERSION.compare(ver.version) === 1 ) { - return; + void showAndLogWarningMessage( + extLogger, + `You are using an unsupported version of the CodeQL CLI (${ver.version.toString()}). ` + + `The minimum supported version is ${OLDEST_SUPPORTED_CLI_VERSION.toString()}. ` + + `Please upgrade to a newer version of the CodeQL CLI.`, + ); + unsupportedWarningShown = true; } - - void showAndLogWarningMessage( - extLogger, - `You are using an unsupported version of the CodeQL CLI (${ver.version.toString()}). ` + - `The minimum supported version is ${CliVersionConstraint.OLDEST_SUPPORTED_CLI_VERSION.toString()}. ` + - `Please upgrade to a newer version of the CodeQL CLI.`, - ); - unsupportedWarningShown = true; }); // Expose the CodeQL CLI features to the extension context under `codeQL.cliFeatures.*`. diff --git a/extensions/ql-vscode/src/query-server/query-server-client.ts b/extensions/ql-vscode/src/query-server/query-server-client.ts index c17f4212d73..b7134d2f481 100644 --- a/extensions/ql-vscode/src/query-server/query-server-client.ts +++ b/extensions/ql-vscode/src/query-server/query-server-client.ts @@ -101,7 +101,7 @@ export class QueryServerClient extends DisposableObject { * queries at once. */ async supportsRunQueriesMethod(): Promise { - return await this.cliServer.cliConstraints.supportsQueryServerRunQueries(); + return await this.cliServer.supportsFeature("queryServerRunQueries"); } /** Stops the query server by disposing of the current server process. */