Skip to content

Commit 8464892

Browse files
committed
Make contextual queries work for fresh installs
This fixes the contextual queries when you are not in a workspace with the submodule and do not have any downloaded packs in the package cache. In that case, the contextual queries would fail because they weren't able to determine which pack belonged to the database. This fixes it by downloading the `codeql/${language}-all` pack in case no dbscheme is found for the database. After the download is complete, it will return the expected value for the qlpacks. This should work in almost all cases (at least for standard languages).
1 parent 146732f commit 8464892

File tree

6 files changed

+159
-28
lines changed

6 files changed

+159
-28
lines changed

extensions/ql-vscode/src/language-support/contextual/location-finder.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,19 @@ import {
99
ResultSetSchema,
1010
} from "../../common/bqrs-cli-types";
1111
import { CodeQLCliServer } from "../../codeql-cli/cli";
12-
import { DatabaseManager, DatabaseItem } from "../../databases/local-databases";
12+
import { DatabaseItem, DatabaseManager } from "../../databases/local-databases";
1313
import { ProgressCallback } from "../../common/vscode/progress";
1414
import { KeyType } from "./key-type";
15-
import { resolveQueries, runContextualQuery } from "./query-resolver";
15+
import {
16+
resolveContextualQlPacksForDatabase,
17+
resolveContextualQueries,
18+
runContextualQuery,
19+
} from "./query-resolver";
1620
import { CancellationToken, LocationLink, Uri } from "vscode";
1721
import { QueryOutputDir } from "../../run-queries-shared";
1822
import { QueryRunner } from "../../query-server";
1923
import { QueryResultType } from "../../query-server/new-messages";
2024
import { fileRangeFromURI } from "./file-range-from-uri";
21-
import { qlpackOfDatabase } from "../../local-queries";
2225

2326
export const SELECT_QUERY_NAME = "#select";
2427
export const SELECTED_SOURCE_FILE = "selectedSourceFile";
@@ -63,11 +66,11 @@ export async function getLocationsForUriString(
6366
return [];
6467
}
6568

66-
const qlpack = await qlpackOfDatabase(cli, db);
69+
const qlpack = await resolveContextualQlPacksForDatabase(cli, db);
6770
const templates = createTemplates(uri.pathWithinSourceArchive);
6871

6972
const links: FullLocationLink[] = [];
70-
for (const query of await resolveQueries(cli, qlpack, keyType)) {
73+
for (const query of await resolveContextualQueries(cli, qlpack, keyType)) {
7174
const results = await runContextualQuery(
7275
query,
7376
db,

extensions/ql-vscode/src/language-support/contextual/query-resolver.ts

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,67 @@ import {
88
} from "./key-type";
99
import { CodeQLCliServer } from "../../codeql-cli/cli";
1010
import { DatabaseItem } from "../../databases/local-databases";
11-
import { resolveQueriesByLanguagePack as resolveLocalQueries } from "../../local-queries/query-resolver";
11+
import {
12+
qlpackOfDatabase,
13+
resolveQueriesByLanguagePack as resolveLocalQueriesByLanguagePack,
14+
} from "../../local-queries/query-resolver";
1215
import { extLogger } from "../../common/logging/vscode";
1316
import { TeeLogger } from "../../common/logging";
1417
import { CancellationToken } from "vscode";
1518
import { ProgressCallback } from "../../common/vscode/progress";
1619
import { CoreCompletedQuery, QueryRunner } from "../../query-server";
1720
import { createLockFileForStandardQuery } from "../../local-queries/standard-queries";
1821

19-
export async function resolveQueries(
22+
/**
23+
* This wil try to determine the qlpacks for a given database. If it can't find a matching
24+
* dbscheme with downloaded packs, it will download the default packs instead.
25+
*
26+
* @param cli The CLI server to use
27+
* @param databaseItem The database item to find the qlpacks for
28+
*/
29+
export async function resolveContextualQlPacksForDatabase(
30+
cli: CodeQLCliServer,
31+
databaseItem: DatabaseItem,
32+
): Promise<QlPacksForLanguage> {
33+
try {
34+
return await qlpackOfDatabase(cli, databaseItem);
35+
} catch (e) {
36+
// If we can't find the qlpacks for the database, use the defaults instead
37+
}
38+
39+
const dbInfo = await cli.resolveDatabase(databaseItem.databaseUri.fsPath);
40+
const primaryLanguage = dbInfo.languages?.[0];
41+
if (!primaryLanguage) {
42+
throw new Error("Unable to determine primary language of database");
43+
}
44+
45+
const libraryPack = `codeql/${primaryLanguage}-all`;
46+
const queryPack = `codeql/${primaryLanguage}-queries`;
47+
48+
await cli.packDownload([libraryPack, queryPack]);
49+
50+
// Return the default packs. If these weren't valid packs, the download would have failed.
51+
return {
52+
dbschemePack: libraryPack,
53+
dbschemePackIsLibraryPack: true,
54+
queryPack,
55+
};
56+
}
57+
58+
export async function resolveContextualQueries(
2059
cli: CodeQLCliServer,
2160
qlpacks: QlPacksForLanguage,
2261
keyType: KeyType,
2362
): Promise<string[]> {
24-
return resolveLocalQueries(cli, qlpacks, nameOfKeyType(keyType), {
25-
kind: kindOfKeyType(keyType),
26-
"tags contain": [tagOfKeyType(keyType)],
27-
});
63+
return resolveLocalQueriesByLanguagePack(
64+
cli,
65+
qlpacks,
66+
nameOfKeyType(keyType),
67+
{
68+
kind: kindOfKeyType(keyType),
69+
"tags contain": [tagOfKeyType(keyType)],
70+
},
71+
);
2872
}
2973

3074
export async function runContextualQuery(

extensions/ql-vscode/src/language-support/contextual/template-provider.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,22 @@ import { KeyType } from "./key-type";
2323
import {
2424
FullLocationLink,
2525
getLocationsForUriString,
26+
SELECTED_SOURCE_COLUMN,
2627
SELECTED_SOURCE_FILE,
2728
SELECTED_SOURCE_LINE,
28-
SELECTED_SOURCE_COLUMN,
2929
} from "./location-finder";
30-
import { resolveQueries, runContextualQuery } from "./query-resolver";
30+
import {
31+
resolveContextualQlPacksForDatabase,
32+
resolveContextualQueries,
33+
runContextualQuery,
34+
} from "./query-resolver";
3135
import {
3236
isCanary,
3337
NO_CACHE_AST_VIEWER,
3438
NO_CACHE_CONTEXTUAL_QUERIES,
3539
} from "../../config";
3640
import { CoreCompletedQuery, QueryRunner } from "../../query-server";
3741
import { AstBuilder } from "../ast-viewer/ast-builder";
38-
import { qlpackOfDatabase } from "../../local-queries";
3942
import { MultiCancellationToken } from "../../common/vscode/multi-cancellation-token";
4043

4144
/**
@@ -248,8 +251,8 @@ export class TemplatePrintAstProvider {
248251
throw new Error("Can't infer database from the provided source.");
249252
}
250253

251-
const qlpacks = await qlpackOfDatabase(this.cli, db);
252-
const queries = await resolveQueries(
254+
const qlpacks = await resolveContextualQlPacksForDatabase(this.cli, db);
255+
const queries = await resolveContextualQueries(
253256
this.cli,
254257
qlpacks,
255258
KeyType.PrintAstQuery,
@@ -336,11 +339,11 @@ export class TemplatePrintCfgProvider {
336339
throw new Error("Can't infer database from the provided source.");
337340
}
338341

339-
const qlpack = await qlpackOfDatabase(this.cli, db);
342+
const qlpack = await resolveContextualQlPacksForDatabase(this.cli, db);
340343
if (!qlpack) {
341344
throw new Error("Can't infer qlpack from database source archive.");
342345
}
343-
const queries = await resolveQueries(
346+
const queries = await resolveContextualQueries(
344347
this.cli,
345348
qlpack,
346349
KeyType.PrintCfgQuery,

extensions/ql-vscode/src/local-queries/query-resolver.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ import { telemetryListener } from "../common/vscode/telemetry";
1616
import { SuiteInstruction } from "../packaging/suite-instruction";
1717
import { QueryConstraints } from "./query-constraints";
1818

19+
/**
20+
* Consider using `resolveContextualQlPacksForDatabase` instead.
21+
* @param cli The CLI server instance to use.
22+
* @param db The database to find the QLPack for.
23+
*/
1924
export async function qlpackOfDatabase(
2025
cli: Pick<CodeQLCliServer, "resolveQlpacks">,
2126
db: Pick<DatabaseItem, "contents">,

extensions/ql-vscode/test/vscode-tests/cli-integration/run-cli.test.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import {
88
} from "../../../src/codeql-cli/cli";
99
import { itWithCodeQL } from "../cli";
1010
import { getOnDiskWorkspaceFolders } from "../../../src/common/vscode/workspace-folders";
11-
import { KeyType, resolveQueries } from "../../../src/language-support";
11+
import {
12+
KeyType,
13+
resolveContextualQueries,
14+
} from "../../../src/language-support";
1215
import { faker } from "@faker-js/faker";
1316
import { getActivatedExtension } from "../global.helper";
1417
import { BaseLogger } from "../../../src/common/logging";
@@ -117,7 +120,11 @@ describe("Use cli", () => {
117120
expect(pack.queryPack).toContain(lang);
118121
}
119122

120-
const result = await resolveQueries(cli, pack, KeyType.PrintAstQuery);
123+
const result = await resolveContextualQueries(
124+
cli,
125+
pack,
126+
KeyType.PrintAstQuery,
127+
);
121128

122129
// It doesn't matter what the name or path of the query is, only
123130
// that we have found exactly one query.

extensions/ql-vscode/test/vscode-tests/no-workspace/language-support/contextual/query-resolver.test.ts

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,97 @@ import { getErrorMessage } from "../../../../../src/common/helpers-pure";
55

66
import * as log from "../../../../../src/common/logging/notifications";
77
import * as workspaceFolders from "../../../../../src/common/vscode/workspace-folders";
8-
import { KeyType, resolveQueries } from "../../../../../src/language-support";
9-
import { CodeQLCliServer } from "../../../../../src/codeql-cli/cli";
8+
import {
9+
KeyType,
10+
resolveContextualQlPacksForDatabase,
11+
resolveContextualQueries,
12+
} from "../../../../../src/language-support";
13+
import { CodeQLCliServer, DbInfo } from "../../../../../src/codeql-cli/cli";
1014
import { mockedObject } from "../../../utils/mocking.helpers";
15+
import * as queryResolver from "../../../../../src/local-queries/query-resolver";
16+
import { DatabaseItem } from "../../../../../src/databases/local-databases";
17+
import { Uri } from "vscode";
1118

1219
describe("queryResolver", () => {
13-
const resolveQueriesInSuite = jest.fn();
20+
let qlpackOfDatabase: jest.SpiedFunction<
21+
typeof queryResolver.qlpackOfDatabase
22+
>;
23+
24+
const resolveQueriesInSuite: jest.MockedFunction<
25+
typeof CodeQLCliServer.prototype.resolveQueriesInSuite
26+
> = jest.fn();
27+
const resolveDatabase: jest.MockedFunction<
28+
typeof CodeQLCliServer.prototype.resolveDatabase
29+
> = jest.fn();
30+
const packDownload: jest.MockedFunction<
31+
typeof CodeQLCliServer.prototype.packDownload
32+
> = jest.fn();
1433

1534
const mockCli = mockedObject<CodeQLCliServer>({
1635
resolveQueriesInSuite,
36+
resolveDatabase,
37+
packDownload,
1738
});
1839

1940
beforeEach(() => {
41+
qlpackOfDatabase = jest.spyOn(queryResolver, "qlpackOfDatabase");
42+
2043
jest
2144
.spyOn(workspaceFolders, "getOnDiskWorkspaceFolders")
2245
.mockReturnValue([]);
2346
jest.spyOn(log, "showAndLogErrorMessage").mockResolvedValue(undefined);
2447
});
2548

26-
describe("resolveQueries", () => {
49+
describe("resolveContextualQlPacksForDatabase", () => {
50+
let databaseItem: DatabaseItem;
51+
52+
beforeEach(() => {
53+
databaseItem = {
54+
name: "my-db",
55+
language: "csharp",
56+
databaseUri: Uri.file("/a/b/c/db"),
57+
} as DatabaseItem;
58+
});
59+
60+
it("should resolve a qlpack when CLI returns qlpack", async () => {
61+
qlpackOfDatabase.mockResolvedValue({
62+
dbschemePack: "dbschemePack",
63+
dbschemePackIsLibraryPack: false,
64+
});
65+
66+
expect(
67+
await resolveContextualQlPacksForDatabase(mockCli, databaseItem),
68+
).toEqual({
69+
dbschemePack: "dbschemePack",
70+
dbschemePackIsLibraryPack: false,
71+
});
72+
});
73+
74+
it("should return qlpack when downloading packs", async () => {
75+
qlpackOfDatabase.mockRejectedValue(new Error("error"));
76+
resolveDatabase.mockResolvedValue({
77+
languages: ["csharp"],
78+
} as DbInfo);
79+
80+
expect(
81+
await resolveContextualQlPacksForDatabase(mockCli, databaseItem),
82+
).toEqual({
83+
dbschemePack: "codeql/csharp-all",
84+
dbschemePackIsLibraryPack: true,
85+
queryPack: "codeql/csharp-queries",
86+
});
87+
expect(packDownload).toHaveBeenCalledTimes(1);
88+
expect(packDownload).toHaveBeenCalledWith([
89+
"codeql/csharp-all",
90+
"codeql/csharp-queries",
91+
]);
92+
});
93+
});
94+
95+
describe("resolveContextualQueries", () => {
2796
it("should resolve a query", async () => {
28-
resolveQueriesInSuite.mockReturnValue(["a", "b"]);
29-
const result = await resolveQueries(
97+
resolveQueriesInSuite.mockResolvedValue(["a", "b"]);
98+
const result = await resolveContextualQueries(
3099
mockCli,
31100
{ dbschemePack: "my-qlpack", dbschemePackIsLibraryPack: false },
32101
KeyType.DefinitionQuery,
@@ -53,10 +122,10 @@ describe("queryResolver", () => {
53122
});
54123

55124
it("should throw an error when there are no queries found", async () => {
56-
resolveQueriesInSuite.mockReturnValue([]);
125+
resolveQueriesInSuite.mockResolvedValue([]);
57126

58127
try {
59-
await resolveQueries(
128+
await resolveContextualQueries(
60129
mockCli,
61130
{ dbschemePack: "my-qlpack", dbschemePackIsLibraryPack: false },
62131
KeyType.DefinitionQuery,

0 commit comments

Comments
 (0)