Skip to content

Commit 348e923

Browse files
author
Dave Bartolomeo
committed
Use LocalQueryRun in LocalQueries
1 parent 543bada commit 348e923

2 files changed

Lines changed: 103 additions & 220 deletions

File tree

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

Lines changed: 93 additions & 203 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,23 @@ import {
2222
tryGetQueryMetadata,
2323
} from "./helpers";
2424
import { displayQuickQuery } from "./quick-query";
25-
import { CoreQueryResults, CoreQueryTarget, QueryRunner } from "./queryRunner";
25+
import { CoreQueryResults, QueryRunner } from "./queryRunner";
2626
import { QueryHistoryManager } from "./query-history/query-history-manager";
2727
import { DatabaseUI } from "./local-databases-ui";
2828
import { ResultsView } from "./interface";
2929
import { DatabaseItem, DatabaseManager } from "./local-databases";
3030
import {
3131
createInitialQueryInfo,
32+
determineSelectedQuery,
3233
EvaluatorLogPaths,
3334
generateEvalLogSummaries,
3435
logEndSummary,
3536
QueryEvaluationInfo,
3637
QueryOutputDir,
3738
QueryWithResults,
39+
SelectedQuery,
3840
} from "./run-queries-shared";
39-
import {
40-
CompletedLocalQueryInfo,
41-
InitialQueryInfo,
42-
LocalQueryInfo,
43-
} from "./query-results";
41+
import { CompletedLocalQueryInfo, LocalQueryInfo } from "./query-results";
4442
import { WebviewReveal } from "./interface-utils";
4543
import { asError, getErrorMessage } from "./pure/helpers-pure";
4644
import { CodeQLCliServer } from "./cli";
@@ -87,7 +85,7 @@ export class LocalQueryRun {
8785
public constructor(
8886
private readonly outputDir: QueryOutputDir,
8987
private readonly localQueries: LocalQueries,
90-
private readonly queryInfo: LocalQueryInfo,
88+
public readonly queryInfo: LocalQueryInfo,
9189
private readonly dbItem: DatabaseItem,
9290
public readonly logger: Logger,
9391
) {}
@@ -101,7 +99,7 @@ export class LocalQueryRun {
10199
* successful or not.
102100
* */
103101
public async complete(results: CoreQueryResults): Promise<void> {
104-
const evalLogPaths = await this.localQueries.summarizeEvalLog(
102+
const evalLogPaths = await this.summarizeEvalLog(
105103
results.resultType,
106104
this.outputDir,
107105
this.logger,
@@ -123,6 +121,38 @@ export class LocalQueryRun {
123121
await this.localQueries.queryHistoryManager.refreshTreeView();
124122
}
125123

124+
/**
125+
* Generate summaries of the structured evaluator log.
126+
*/
127+
private async summarizeEvalLog(
128+
resultType: QueryResultType,
129+
outputDir: QueryOutputDir,
130+
logger: BaseLogger,
131+
): Promise<EvaluatorLogPaths | undefined> {
132+
const evalLogPaths = await generateEvalLogSummaries(
133+
this.localQueries.cliServer,
134+
outputDir,
135+
);
136+
if (evalLogPaths !== undefined) {
137+
if (evalLogPaths.endSummary !== undefined) {
138+
void logEndSummary(evalLogPaths.endSummary, logger); // Logged asynchrnously
139+
}
140+
} else {
141+
// Raw evaluator log was not found. Notify the user, unless we know why it wasn't found.
142+
if (resultType === QueryResultType.SUCCESS) {
143+
void showAndLogWarningMessage(
144+
`Failed to write structured evaluator log to ${outputDir.evalLogPath}.`,
145+
);
146+
} else {
147+
// Don't bother notifying the user if there's no log. For some errors, like compilation
148+
// errors, we don't expect a log. For cancellations and OOM errors, whether or not we have
149+
// a log depends on how far execution got before termination.
150+
}
151+
}
152+
153+
return evalLogPaths;
154+
}
155+
126156
/**
127157
* Gets a `QueryWithResults` containing information about the evaluation of the query and its
128158
* result, in the form expected by the query history UI.
@@ -342,93 +372,94 @@ export class LocalQueries extends DisposableObject {
342372
* object to update the UI based on the results of the query.
343373
*/
344374
public async createLocalQueryRun(
345-
queryPath: string,
346-
quickEval: boolean,
347-
range: Range | undefined,
375+
selectedQuery: SelectedQuery,
348376
dbItem: DatabaseItem,
349-
outputDir: string,
377+
outputDir: QueryOutputDir,
350378
tokenSource: CancellationTokenSource,
351379
): Promise<LocalQueryRun> {
352-
const queryOutputDir = new QueryOutputDir(outputDir);
380+
await createTimestampFile(outputDir.querySaveDir);
353381

354-
await createTimestampFile(outputDir);
382+
if (this.queryRunner.customLogDirectory) {
383+
void showAndLogWarningMessage(
384+
`Custom log directories are no longer supported. The "codeQL.runningQueries.customLogDirectory" setting is deprecated. Unset the setting to stop seeing this message. Query logs saved to ${outputDir.logPath}`,
385+
);
386+
}
355387

356-
const initialInfo = await createInitialQueryInfo(
357-
Uri.file(queryPath),
358-
{
359-
databaseUri: dbItem.databaseUri.toString(),
360-
name: dbItem.name,
361-
},
362-
quickEval,
363-
range,
364-
);
388+
const initialInfo = await createInitialQueryInfo(selectedQuery, {
389+
databaseUri: dbItem.databaseUri.toString(),
390+
name: dbItem.name,
391+
});
365392

366393
// When cancellation is requested from the query history view, we just stop the debug session.
367394
const queryInfo = new LocalQueryInfo(initialInfo, tokenSource);
368395
this.queryHistoryManager.addQuery(queryInfo);
369396

370-
const logger = new TeeLogger(
371-
this.queryRunner.logger,
372-
queryOutputDir.logPath,
373-
);
374-
return new LocalQueryRun(queryOutputDir, this, queryInfo, dbItem, logger);
397+
const logger = new TeeLogger(this.queryRunner.logger, outputDir.logPath);
398+
return new LocalQueryRun(outputDir, this, queryInfo, dbItem, logger);
375399
}
376400

377401
public async compileAndRunQuery(
378402
quickEval: boolean,
379-
selectedQuery: Uri | undefined,
403+
queryUri: Uri | undefined,
380404
progress: ProgressCallback,
381405
token: CancellationToken,
382406
databaseItem: DatabaseItem | undefined,
383407
range?: Range,
384408
): Promise<void> {
385409
if (this.queryRunner !== undefined) {
410+
const selectedQuery = await determineSelectedQuery(
411+
queryUri,
412+
quickEval,
413+
range,
414+
);
415+
386416
// If no databaseItem is specified, use the database currently selected in the Databases UI
387417
databaseItem =
388418
databaseItem ||
389419
(await this.databaseUI.getDatabaseItem(progress, token));
390420
if (databaseItem === undefined) {
391421
throw new Error("Can't run query without a selected database");
392422
}
393-
const databaseInfo = {
394-
name: databaseItem.name,
395-
databaseUri: databaseItem.databaseUri.toString(),
396-
};
423+
424+
const coreQueryRun = this.queryRunner.createQueryRun(
425+
databaseItem.databaseUri.fsPath,
426+
{
427+
queryPath: selectedQuery.queryPath,
428+
quickEvalPosition: selectedQuery.quickEvalPosition,
429+
},
430+
true,
431+
getOnDiskWorkspaceFolders(),
432+
this.queryStorageDir,
433+
undefined,
434+
undefined,
435+
);
397436

398437
// handle cancellation from the history view.
399438
const source = new CancellationTokenSource();
400-
token.onCancellationRequested(() => source.cancel());
401-
402-
const initialInfo = await createInitialQueryInfo(
403-
selectedQuery,
404-
databaseInfo,
405-
quickEval,
406-
range,
407-
);
408-
const item = new LocalQueryInfo(initialInfo, source);
409-
this.queryHistoryManager.addQuery(item);
410439
try {
411-
const completedQueryInfo = await this.compileAndRunQueryAgainstDatabase(
440+
token.onCancellationRequested(() => source.cancel());
441+
442+
const localQueryRun = await this.createLocalQueryRun(
443+
selectedQuery,
412444
databaseItem,
413-
initialInfo,
414-
this.queryStorageDir,
415-
progress,
416-
source.token,
417-
undefined,
418-
item,
445+
coreQueryRun.outputDir,
446+
source,
419447
);
420-
this.queryHistoryManager.completeQuery(item, completedQueryInfo);
421-
await this.showResultsForCompletedQuery(
422-
item as CompletedLocalQueryInfo,
423-
WebviewReveal.Forced,
424-
);
425-
// Note we must update the query history view after showing results as the
426-
// display and sorting might depend on the number of results
427-
} catch (e) {
428-
const err = asError(e);
429-
err.message = `Error running query: ${err.message}`;
430-
item.failureReason = err.message;
431-
throw e;
448+
449+
try {
450+
const results = await coreQueryRun.evaluate(
451+
progress,
452+
source.token,
453+
localQueryRun.logger,
454+
);
455+
456+
await localQueryRun.complete(results);
457+
} catch (e) {
458+
const err = asError(e);
459+
err.message = `Error running query: ${err.message}`;
460+
localQueryRun.queryInfo.failureReason = err.message;
461+
throw e;
462+
}
432463
} finally {
433464
await this.queryHistoryManager.refreshTreeView();
434465
source.dispose();
@@ -510,145 +541,4 @@ export class LocalQueries extends DisposableObject {
510541
): Promise<void> {
511542
await this.localQueryResultsView.showResults(query, forceReveal, false);
512543
}
513-
514-
private async compileAndRunQueryAgainstDatabase(
515-
db: DatabaseItem,
516-
initialInfo: InitialQueryInfo,
517-
queryStorageDir: string,
518-
progress: ProgressCallback,
519-
token: CancellationToken,
520-
templates?: Record<string, string>,
521-
queryInfo?: LocalQueryInfo, // May be omitted for queries not initiated by the user. If omitted we won't create a structured log for the query.
522-
): Promise<QueryWithResults> {
523-
const queryTarget: CoreQueryTarget = {
524-
queryPath: initialInfo.queryPath,
525-
quickEvalPosition: initialInfo.quickEvalPosition,
526-
};
527-
528-
const diskWorkspaceFolders = getOnDiskWorkspaceFolders();
529-
const queryRun = this.queryRunner.createQueryRun(
530-
db.databaseUri.fsPath,
531-
queryTarget,
532-
queryInfo !== undefined,
533-
diskWorkspaceFolders,
534-
queryStorageDir,
535-
initialInfo.id,
536-
templates,
537-
);
538-
539-
await createTimestampFile(queryRun.outputDir.querySaveDir);
540-
541-
const logPath = queryRun.outputDir.logPath;
542-
if (this.queryRunner.customLogDirectory) {
543-
void showAndLogWarningMessage(
544-
`Custom log directories are no longer supported. The "codeQL.runningQueries.customLogDirectory" setting is deprecated. Unset the setting to stop seeing this message. Query logs saved to ${logPath}`,
545-
);
546-
}
547-
548-
const logger = new TeeLogger(this.queryRunner.logger, logPath);
549-
const coreResults = await queryRun.evaluate(progress, token, logger);
550-
if (queryInfo !== undefined) {
551-
const evalLogPaths = await this.summarizeEvalLog(
552-
coreResults.resultType,
553-
queryRun.outputDir,
554-
logger,
555-
);
556-
if (evalLogPaths !== undefined) {
557-
queryInfo.setEvaluatorLogPaths(evalLogPaths);
558-
}
559-
}
560-
561-
return await this.getCompletedQueryInfo(
562-
db,
563-
queryTarget,
564-
queryRun.outputDir,
565-
coreResults,
566-
);
567-
}
568-
569-
/**
570-
* Generate summaries of the structured evaluator log.
571-
*/
572-
public async summarizeEvalLog(
573-
resultType: QueryResultType,
574-
outputDir: QueryOutputDir,
575-
logger: BaseLogger,
576-
): Promise<EvaluatorLogPaths | undefined> {
577-
const evalLogPaths = await generateEvalLogSummaries(
578-
this.cliServer,
579-
outputDir,
580-
);
581-
if (evalLogPaths !== undefined) {
582-
if (evalLogPaths.endSummary !== undefined) {
583-
void logEndSummary(evalLogPaths.endSummary, logger); // Logged asynchrnously
584-
}
585-
} else {
586-
// Raw evaluator log was not found. Notify the user, unless we know why it wasn't found.
587-
switch (resultType) {
588-
case QueryResultType.COMPILATION_ERROR:
589-
case QueryResultType.DBSCHEME_MISMATCH_NAME:
590-
case QueryResultType.DBSCHEME_NO_UPGRADE:
591-
// In these cases, the evaluator was never invoked anyway, so don't bother warning.
592-
break;
593-
594-
default:
595-
void showAndLogWarningMessage(
596-
`Failed to write structured evaluator log to ${outputDir.evalLogPath}.`,
597-
);
598-
break;
599-
}
600-
}
601-
602-
return evalLogPaths;
603-
}
604-
605-
/**
606-
* Gets a `QueryWithResults` containing information about the evaluation of the query and its
607-
* result, in the form expected by the query history UI.
608-
*/
609-
private async getCompletedQueryInfo(
610-
dbItem: DatabaseItem,
611-
queryTarget: CoreQueryTarget,
612-
outputDir: QueryOutputDir,
613-
results: CoreQueryResults,
614-
): Promise<QueryWithResults> {
615-
// Read the query metadata if possible, to use in the UI.
616-
const metadata = await tryGetQueryMetadata(
617-
this.cliServer,
618-
queryTarget.queryPath,
619-
);
620-
const query = new QueryEvaluationInfo(
621-
outputDir.querySaveDir,
622-
dbItem.databaseUri.fsPath,
623-
await dbItem.hasMetadataFile(),
624-
queryTarget.quickEvalPosition,
625-
metadata,
626-
);
627-
628-
if (results.resultType !== QueryResultType.SUCCESS) {
629-
const message = results.message
630-
? redactableError`${results.message}`
631-
: redactableError`Failed to run query`;
632-
void extLogger.log(message.fullMessage);
633-
void showAndLogExceptionWithTelemetry(
634-
redactableError`Failed to run query: ${message}`,
635-
);
636-
}
637-
const message = formatResultMessage(results);
638-
const successful = results.resultType === QueryResultType.SUCCESS;
639-
return {
640-
query,
641-
result: {
642-
evaluationTime: results.evaluationTime,
643-
queryId: 0,
644-
resultType: successful
645-
? QueryResultType.SUCCESS
646-
: QueryResultType.OTHER_ERROR,
647-
runId: 0,
648-
message,
649-
},
650-
message,
651-
successful,
652-
};
653-
}
654544
}

0 commit comments

Comments
 (0)