Skip to content

Commit 9387d55

Browse files
committed
Unify model generation query running
1 parent 8a8a85f commit 9387d55

File tree

4 files changed

+211
-263
lines changed

4 files changed

+211
-263
lines changed
Lines changed: 69 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,14 @@
1-
import { CancellationToken } from "vscode";
2-
import { DatabaseItem } from "../databases/local-databases";
31
import { basename } from "path";
4-
import { QueryRunner } from "../query-server";
5-
import { CodeQLCliServer } from "../codeql-cli/cli";
2+
import { BaseLogger } from "../common/logging";
63
import {
7-
NotificationLogger,
8-
showAndLogExceptionWithTelemetry,
9-
} from "../common/logging";
10-
import { getModelsAsDataLanguage } from "./languages";
11-
import { ProgressCallback } from "../common/vscode/progress";
12-
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
13-
import { ModeledMethod, ModeledMethodType } from "./modeled-method";
14-
import { redactableError } from "../common/errors";
15-
import { telemetryListener } from "../common/vscode/telemetry";
16-
import { runQuery } from "../local-queries/run-query";
17-
import { resolveQueries } from "../local-queries";
4+
getModelsAsDataLanguage,
5+
ModelsAsDataLanguage,
6+
ModelsAsDataLanguageModelType,
7+
} from "./languages";
8+
import { ModeledMethod } from "./modeled-method";
189
import { QueryLanguage } from "../common/query-language";
10+
import { GenerateQueriesOptions, runGenerateQueries } from "./generate";
11+
import { DecodedBqrs } from "../common/bqrs-cli-types";
1912

2013
const FLOW_MODEL_SUPPORTED_LANGUAGES = [
2114
QueryLanguage.CSharp,
@@ -28,159 +21,47 @@ export function isFlowModelGenerationSupported(
2821
return FLOW_MODEL_SUPPORTED_LANGUAGES.includes(language);
2922
}
3023

31-
type FlowModelOptions = {
32-
cliServer: CodeQLCliServer;
33-
queryRunner: QueryRunner;
34-
logger: NotificationLogger;
35-
queryStorageDir: string;
36-
databaseItem: DatabaseItem;
24+
type FlowModelOptions = GenerateQueriesOptions & {
25+
logger: BaseLogger;
3726
language: QueryLanguage;
38-
progress: ProgressCallback;
39-
token: CancellationToken;
40-
onResults: (results: ModeledMethod[]) => void | Promise<void>;
4127
};
4228

43-
export async function runFlowModelQueries({
44-
onResults,
45-
...options
46-
}: FlowModelOptions) {
47-
const queries = await resolveFlowQueries(
48-
options.cliServer,
49-
options.databaseItem,
50-
);
51-
52-
const queriesByBasename: Record<string, string> = {};
53-
for (const query of queries) {
54-
queriesByBasename[basename(query)] = query;
55-
}
56-
57-
const summaryResults = await runSingleFlowQuery(
58-
"summary",
59-
queriesByBasename["CaptureSummaryModels.ql"],
60-
0,
61-
options,
62-
);
63-
if (summaryResults) {
64-
await onResults(summaryResults);
65-
}
66-
67-
const sinkResults = await runSingleFlowQuery(
68-
"sink",
69-
queriesByBasename["CaptureSinkModels.ql"],
70-
1,
71-
options,
72-
);
73-
if (sinkResults) {
74-
await onResults(sinkResults);
75-
}
76-
77-
const sourceResults = await runSingleFlowQuery(
78-
"source",
79-
queriesByBasename["CaptureSourceModels.ql"],
80-
2,
81-
options,
82-
);
83-
if (sourceResults) {
84-
await onResults(sourceResults);
85-
}
86-
87-
const neutralResults = await runSingleFlowQuery(
88-
"neutral",
89-
queriesByBasename["CaptureNeutralModels.ql"],
90-
3,
91-
options,
92-
);
93-
if (neutralResults) {
94-
await onResults(neutralResults);
95-
}
96-
}
97-
98-
async function resolveFlowQueries(
99-
cliServer: CodeQLCliServer,
100-
databaseItem: DatabaseItem,
101-
): Promise<string[]> {
102-
const packsToSearch = [`codeql/${databaseItem.language}-queries`];
103-
104-
return await resolveQueries(
105-
cliServer,
106-
packsToSearch,
107-
"flow model generator",
108-
{
109-
"tags contain": ["modelgenerator"],
110-
},
111-
);
112-
}
29+
const queriesToModel: Record<string, ModelsAsDataLanguageModelType> = {
30+
"CaptureSummaryModels.ql": "summary",
31+
"CaptureSinkModels.ql": "sink",
32+
"CaptureSourceModels.ql": "source",
33+
"CaptureNeutralModels.ql": "neutral",
34+
};
11335

114-
async function runSingleFlowQuery(
115-
type: Exclude<ModeledMethodType, "none">,
116-
queryPath: string | undefined,
117-
queryStep: number,
118-
{
119-
cliServer,
120-
queryRunner,
121-
logger,
122-
queryStorageDir,
123-
databaseItem,
124-
language,
125-
progress,
126-
token,
127-
}: Omit<FlowModelOptions, "onResults">,
128-
): Promise<ModeledMethod[]> {
129-
// Check that the right query was found
130-
if (queryPath === undefined) {
131-
void showAndLogExceptionWithTelemetry(
132-
logger,
133-
telemetryListener,
134-
redactableError`Failed to find ${type} query`,
36+
function parseFlowModelResults(
37+
queryPath: string,
38+
bqrs: DecodedBqrs,
39+
modelsAsDataLanguage: ModelsAsDataLanguage,
40+
logger: BaseLogger,
41+
): ModeledMethod[] {
42+
if (Object.keys(bqrs).length !== 1) {
43+
throw new Error(
44+
`Expected exactly one result set from ${queryPath}, but got ${
45+
Object.keys(bqrs).length
46+
}`,
13547
);
136-
return [];
13748
}
13849

139-
// Run the query
140-
const completedQuery = await runQuery({
141-
queryRunner,
142-
databaseItem,
143-
queryPath,
144-
queryStorageDir,
145-
additionalPacks: getOnDiskWorkspaceFolders(),
146-
extensionPacks: undefined,
147-
progress: ({ step, message }) =>
148-
progress({
149-
message: `Generating ${type} model: ${message}`,
150-
step: queryStep * 1000 + step,
151-
maxStep: 4000,
152-
}),
153-
token,
154-
});
155-
156-
if (!completedQuery) {
50+
const modelType = queriesToModel[basename(queryPath)];
51+
if (!modelType) {
52+
void logger.log(`Unknown model type for ${queryPath}`);
15753
return [];
15854
}
15955

160-
// Interpret the results
161-
const modelsAsDataLanguage = getModelsAsDataLanguage(language);
162-
163-
const definition = modelsAsDataLanguage.predicates[type];
56+
const resultSet = bqrs[Object.keys(bqrs)[0]];
16457

165-
const bqrsPath = completedQuery.outputDir.bqrsPath;
58+
const results = resultSet.tuples;
16659

167-
const bqrsInfo = await cliServer.bqrsInfo(bqrsPath);
168-
if (bqrsInfo["result-sets"].length !== 1) {
169-
void showAndLogExceptionWithTelemetry(
170-
logger,
171-
telemetryListener,
172-
redactableError`Expected exactly one result set, got ${
173-
bqrsInfo["result-sets"].length
174-
} for ${basename(queryPath)}`,
175-
);
60+
const definition = modelsAsDataLanguage.predicates[modelType];
61+
if (!definition) {
62+
throw new Error(`No definition for ${modelType}`);
17663
}
17764

178-
const resultSet = bqrsInfo["result-sets"][0];
179-
180-
const decodedResults = await cliServer.bqrsDecode(bqrsPath, resultSet.name);
181-
182-
const results = decodedResults.tuples;
183-
18465
return (
18566
results
18667
// This is just a sanity check. The query should only return strings.
@@ -192,3 +73,37 @@ async function runSingleFlowQuery(
19273
})
19374
);
19475
}
76+
77+
export async function runFlowModelQueries({
78+
cliServer,
79+
queryRunner,
80+
logger,
81+
queryStorageDir,
82+
databaseItem,
83+
language,
84+
progress,
85+
token,
86+
onResults,
87+
}: FlowModelOptions) {
88+
const modelsAsDataLanguage = getModelsAsDataLanguage(language);
89+
90+
return runGenerateQueries(
91+
{
92+
queryConstraints: {
93+
"tags contain": ["modelgenerator"],
94+
},
95+
filterQueries: (queryPath) => basename(queryPath) in queriesToModel,
96+
parseResults: (queryPath, results) =>
97+
parseFlowModelResults(queryPath, results, modelsAsDataLanguage, logger),
98+
},
99+
{
100+
cliServer,
101+
queryRunner,
102+
queryStorageDir,
103+
databaseItem,
104+
progress,
105+
token,
106+
onResults,
107+
},
108+
);
109+
}

0 commit comments

Comments
 (0)