General refactoring and adding comments

There is no new behaviour added in this commit. Just some cleanup:

- Move some shared constants to the `helpers` module
- Add comments to some of the query related modules
- Some general formatting and tidying
This commit is contained in:
Andrew Eisenberg 2022-02-09 19:06:28 -08:00
Родитель 95e818898e
Коммит b53657344c
18 изменённых файлов: 155 добавлений и 100 удалений

Просмотреть файл

@ -35,7 +35,7 @@
- Fix a bug with importing large databases. Databases over 4GB can now be imported directly from LGTM or from a zip file. This functionality is only available when using CodeQL CLI version 2.6.0 or later. [#971](https://github.com/github/vscode-codeql/pull/971)
- Replace certain control codes (`U+0000` - `U+001F`) with their corresponding control labels (`U+2400` - `U+241F`) in the results view. [#963](https://github.com/github/vscode-codeql/pull/963)
- Allow case-insensitive project slugs for GitHub repositories when adding a CodeQL database from LGTM. [#978](https://github.com/github/vscode-codeql/pull/961)
- Add a _CodeQL: Preview Query Help_ command to generate Markdown previews of `.qhelp` query help files. This command should only be run in trusted workspaces. See https://codeql.github.com/docs/codeql-cli/testing-query-help-files for more information about query help. [#988](https://github.com/github/vscode-codeql/pull/988)
- Add a _CodeQL: Preview Query Help_ command to generate Markdown previews of `.qhelp` query help files. This command should only be run in trusted workspaces. See [the CodeQL CLI docs](https://codeql.github.com/docs/codeql-cli/testing-query-help-files) for more information about query help. [#988](https://github.com/github/vscode-codeql/pull/988)
- Make "Open Referenced File" command accessible from the active editor menu. [#989](https://github.com/github/vscode-codeql/pull/989)
- Fix a bug where result set names in the result set drop-down were disappearing when viewing a sorted table. [#1007](https://github.com/github/vscode-codeql/pull/1007)
- Allow query result locations with 0 as the end column value. These are treated as the first column in the line. [#1002](https://github.com/github/vscode-codeql/pull/1002)

Просмотреть файл

@ -8,7 +8,7 @@ import {
} from 'vscode';
import * as path from 'path';
import { tmpDir } from '../run-queries';
import { tmpDir } from '../helpers';
import {
FromCompareViewMessage,
ToCompareViewMessage,

Просмотреть файл

@ -2,8 +2,7 @@ import { DisposableObject } from './pure/disposable-object';
import { workspace, Event, EventEmitter, ConfigurationChangeEvent, ConfigurationTarget } from 'vscode';
import { DistributionManager } from './distribution';
import { logger } from './logging';
const ONE_DAY_IN_MS = 24 * 60 * 60 * 1000;
import { ONE_DAY_IN_MS } from './pure/helpers-pure';
/** Helper class to look up a labelled (and possibly nested) setting. */
export class Setting {
@ -258,7 +257,7 @@ export class QueryHistoryConfigListener extends ConfigListener implements QueryH
}
/**
* The configuration value is in days, but return the value in milliseconds.
* The configuration value is in days, but return the value in milliseconds to make it easier to use.
*/
public get ttlInMillis(): number {
return (QUERY_HISTORY_TTL.getValue<number>() || 30) * ONE_DAY_IN_MS;

Просмотреть файл

@ -20,7 +20,7 @@ import {
ProgressCallback,
} from './commandRunner';
import { logger } from './logging';
import { tmpDir } from './run-queries';
import { tmpDir } from './helpers';
/**
* Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file.
@ -434,7 +434,7 @@ function convertRawLgtmSlug(maybeSlug: string): string | undefined {
}
return;
}
function extractProjectSlug(lgtmUrl: string): string | undefined {
// Only matches the '/g/' provider (github)
const re = new RegExp('https://lgtm.com/projects/g/(.*[^/])');

Просмотреть файл

@ -54,7 +54,15 @@ import {
GithubApiError,
GithubRateLimitedError
} from './distribution';
import * as helpers from './helpers';
import {
findLanguage,
tmpDirDisposal,
showBinaryChoiceDialog,
showAndLogErrorMessage,
showAndLogWarningMessage,
showAndLogInformationMessage,
showInformationMessageWithAction
} from './helpers';
import { assertNever } from './pure/helpers-pure';
import { spawnIdeServer } from './ide-server';
import { InterfaceManager } from './interface';
@ -64,7 +72,7 @@ import { QueryHistoryManager } from './query-history';
import { FullCompletedQueryInfo, FullQueryInfo } from './query-results';
import * as qsClient from './queryserver-client';
import { displayQuickQuery } from './quick-query';
import { compileAndRunQueryAgainstDatabase, createInitialQueryInfo, tmpDirDisposal } from './run-queries';
import { compileAndRunQueryAgainstDatabase, createInitialQueryInfo } from './run-queries';
import { QLTestAdapterFactory } from './test-adapter';
import { TestUIService } from './test-ui';
import { CompareInterfaceManager } from './compare/compare-interface';
@ -189,7 +197,7 @@ export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionIn
const shouldUpdateOnNextActivationKey = 'shouldUpdateOnNextActivation';
registerErrorStubs([checkForUpdatesCommand], command => (async () => {
void helpers.showAndLogErrorMessage(`Can't execute ${command}: waiting to finish loading CodeQL CLI.`);
void showAndLogErrorMessage(`Can't execute ${command}: waiting to finish loading CodeQL CLI.`);
}));
interface DistributionUpdateConfig {
@ -201,7 +209,7 @@ export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionIn
async function installOrUpdateDistributionWithProgressTitle(progressTitle: string, config: DistributionUpdateConfig): Promise<void> {
const minSecondsSinceLastUpdateCheck = config.isUserInitiated ? 0 : 86400;
const noUpdatesLoggingFunc = config.shouldDisplayMessageWhenNoUpdates ?
helpers.showAndLogInformationMessage : async (message: string) => void logger.log(message);
showAndLogInformationMessage : async (message: string) => void logger.log(message);
const result = await distributionManager.checkForUpdatesToExtensionManagedDistribution(minSecondsSinceLastUpdateCheck);
// We do want to auto update if there is no distribution at all
@ -223,7 +231,7 @@ export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionIn
const updateAvailableMessage = `Version "${result.updatedRelease.name}" of the CodeQL CLI is now available. ` +
'Do you wish to upgrade?';
await ctx.globalState.update(shouldUpdateOnNextActivationKey, true);
if (await helpers.showInformationMessageWithAction(updateAvailableMessage, 'Restart and Upgrade')) {
if (await showInformationMessageWithAction(updateAvailableMessage, 'Restart and Upgrade')) {
await commands.executeCommand('workbench.action.reloadWindow');
}
} else {
@ -236,7 +244,7 @@ export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionIn
distributionManager.installExtensionManagedDistributionRelease(result.updatedRelease, progress));
await ctx.globalState.update(shouldUpdateOnNextActivationKey, false);
void helpers.showAndLogInformationMessage(`CodeQL CLI updated to version "${result.updatedRelease.name}".`);
void showAndLogInformationMessage(`CodeQL CLI updated to version "${result.updatedRelease.name}".`);
}
break;
default:
@ -263,7 +271,7 @@ export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionIn
// Don't rethrow the exception, because if the config is changed, we want to be able to retry installing
// or updating the distribution.
const alertFunction = (codeQlInstalled && !config.isUserInitiated) ?
helpers.showAndLogWarningMessage : helpers.showAndLogErrorMessage;
showAndLogWarningMessage : showAndLogErrorMessage;
const taskDescription = (willUpdateCodeQl ? 'update' :
codeQlInstalled ? 'check for updates to' : 'install') + ' CodeQL CLI';
@ -298,20 +306,20 @@ export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionIn
}
})();
void helpers.showAndLogWarningMessage(
void showAndLogWarningMessage(
`The current version of the CodeQL CLI (${result.version.raw}) ` +
`is incompatible with this extension. ${fixGuidanceMessage}`
);
break;
}
case FindDistributionResultKind.UnknownCompatibilityDistribution:
void helpers.showAndLogWarningMessage(
void showAndLogWarningMessage(
'Compatibility with the configured CodeQL CLI could not be determined. ' +
'You may experience problems using the extension.'
);
break;
case FindDistributionResultKind.NoDistribution:
void helpers.showAndLogErrorMessage('The CodeQL CLI could not be found.');
void showAndLogErrorMessage('The CodeQL CLI could not be found.');
break;
default:
assertNever(result);
@ -338,7 +346,7 @@ export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionIn
} else if (distributionResult.kind === FindDistributionResultKind.NoDistribution) {
registerErrorStubs([checkForUpdatesCommand], command => async () => {
const installActionName = 'Install CodeQL CLI';
const chosenAction = await void helpers.showAndLogErrorMessage(`Can't execute ${command}: missing CodeQL CLI.`, {
const chosenAction = await void showAndLogErrorMessage(`Can't execute ${command}: missing CodeQL CLI.`, {
items: [installActionName]
});
if (chosenAction === installActionName) {
@ -477,7 +485,7 @@ async function activateWithInstalledDistribution(
try {
await cmpm.showResults(from, to);
} catch (e) {
void helpers.showAndLogErrorMessage(e.message);
void showAndLogErrorMessage(e.message);
}
}
@ -559,7 +567,7 @@ async function activateWithInstalledDistribution(
const errorMessage = err.message.includes('Generating qhelp in markdown') ? (
`Could not generate markdown from ${pathToQhelp}: Bad formatting in .qhelp file.`
) : `Could not open a preview of the generated file (${absolutePathToMd}).`;
void helpers.showAndLogErrorMessage(errorMessage, { fullMessage: `${errorMessage}\n${err}` });
void showAndLogErrorMessage(errorMessage, { fullMessage: `${errorMessage}\n${err}` });
}
}
@ -576,7 +584,7 @@ async function activateWithInstalledDistribution(
const uri = Uri.file(resolved.resolvedPath);
await window.showTextDocument(uri, { preview: false });
} else {
void helpers.showAndLogErrorMessage(
void showAndLogErrorMessage(
'Jumping from a .qlref file to the .ql file it references is not '
+ 'supported with the CLI version you are running.\n'
+ `Please upgrade your CLI to version ${CliVersionConstraint.CLI_VERSION_WITH_RESOLVE_QLREF
@ -646,15 +654,15 @@ async function activateWithInstalledDistribution(
) => {
let filteredDBs = dbm.databaseItems;
if (filteredDBs.length === 0) {
void helpers.showAndLogErrorMessage('No databases found. Please add a suitable database to your workspace.');
void showAndLogErrorMessage('No databases found. Please add a suitable database to your workspace.');
return;
}
// If possible, only show databases with the right language (otherwise show all databases).
const queryLanguage = await helpers.findLanguage(cliServer, uri);
const queryLanguage = await findLanguage(cliServer, uri);
if (queryLanguage) {
filteredDBs = dbm.databaseItems.filter(db => db.language === queryLanguage);
if (filteredDBs.length === 0) {
void helpers.showAndLogErrorMessage(`No databases found for language ${queryLanguage}. Please add a suitable database to your workspace.`);
void showAndLogErrorMessage(`No databases found for language ${queryLanguage}. Please add a suitable database to your workspace.`);
return;
}
}
@ -686,12 +694,12 @@ async function activateWithInstalledDistribution(
}
if (skippedDatabases.length > 0) {
void logger.log(`Errors:\n${errors.join('\n')}`);
void helpers.showAndLogWarningMessage(
void showAndLogWarningMessage(
`The following databases were skipped:\n${skippedDatabases.join('\n')}.\nFor details about the errors, see the logs.`
);
}
} else {
void helpers.showAndLogErrorMessage('No databases selected.');
void showAndLogErrorMessage('No databases selected.');
}
},
{
@ -718,7 +726,7 @@ async function activateWithInstalledDistribution(
// files may be hidden from the user.
if (dirFound) {
const fileString = files.map(file => path.basename(file)).join(', ');
const res = await helpers.showBinaryChoiceDialog(
const res = await showBinaryChoiceDialog(
`You are about to run ${files.length} queries: ${fileString} Do you want to continue?`
);
if (!res) {
@ -882,7 +890,7 @@ async function activateWithInstalledDistribution(
token: CancellationToken
) => {
await qs.restartQueryServer(progress, token);
void helpers.showAndLogInformationMessage('CodeQL Query Server restarted.', {
void showAndLogInformationMessage('CodeQL Query Server restarted.', {
outputLogger: queryServerLogger,
});
}, {
@ -938,7 +946,7 @@ async function activateWithInstalledDistribution(
commandRunner('codeQL.copyVersion', async () => {
const text = `CodeQL extension version: ${extension?.packageJSON.version} \nCodeQL CLI version: ${await getCliVersion()} \nPlatform: ${os.platform()} ${os.arch()}`;
await env.clipboard.writeText(text);
void helpers.showAndLogInformationMessage(text);
void showAndLogInformationMessage(text);
}));
const getCliVersion = async () => {
@ -960,7 +968,7 @@ async function activateWithInstalledDistribution(
const credentials = await Credentials.initialize(ctx);
const octokit = await credentials.getOctokit();
const userInfo = await octokit.users.getAuthenticated();
void helpers.showAndLogInformationMessage(`Authenticated to GitHub as user: ${userInfo.data.login}`);
void showAndLogInformationMessage(`Authenticated to GitHub as user: ${userInfo.data.login}`);
}
}));

Просмотреть файл

@ -2,6 +2,7 @@ import * as fs from 'fs-extra';
import * as glob from 'glob-promise';
import * as yaml from 'js-yaml';
import * as path from 'path';
import * as tmp from 'tmp-promise';
import {
ExtensionContext,
Uri,
@ -14,6 +15,17 @@ import { UserCancellationException } from './commandRunner';
import { logger } from './logging';
import { QueryMetadata } from './pure/interface-types';
// Shared temporary folder for the extension.
export const tmpDir = tmp.dirSync({ prefix: 'queries_', keep: false, unsafeCleanup: true });
export const upgradesTmpDir = path.join(tmpDir.name, 'upgrades');
fs.ensureDirSync(upgradesTmpDir);
export const tmpDirDisposal = {
dispose: () => {
tmpDir.removeCallback();
}
};
/**
* Show an error message and log it to the console
*

Просмотреть файл

@ -14,7 +14,7 @@ import {
import * as cli from './cli';
import { CodeQLCliServer } from './cli';
import { DatabaseEventKind, DatabaseItem, DatabaseManager } from './databases';
import { showAndLogErrorMessage } from './helpers';
import { showAndLogErrorMessage, tmpDir } from './helpers';
import { assertNever } from './pure/helpers-pure';
import {
FromResultsViewMsg,
@ -33,7 +33,7 @@ import { Logger } from './logging';
import * as messages from './pure/messages';
import { commandRunner } from './commandRunner';
import { CompletedQueryInfo, interpretResults } from './query-results';
import { QueryEvaluationInfo, tmpDir } from './run-queries';
import { QueryEvaluationInfo } from './run-queries';
import { parseSarifLocation, parseSarifPlainTextMessage } from './pure/sarif-utils';
import {
WebviewReveal,

Просмотреть файл

@ -29,3 +29,8 @@ export const asyncFilter = async function <T>(arr: T[], predicate: (arg0: T) =>
const results = await Promise.all(arr.map(predicate));
return arr.filter((_, index) => results[index]);
};
export const ONE_DAY_IN_MS = 24 * 60 * 60 * 1000;
export const ONE_HOUR_IN_MS = 1000 * 60 * 60;
export const TWO_HOURS_IN_MS = 1000 * 60 * 60 * 2;
export const THREE_HOURS_IN_MS = 1000 * 60 * 60 * 3;

Просмотреть файл

@ -28,7 +28,7 @@ import { URLSearchParams } from 'url';
import { QueryServerClient } from './queryserver-client';
import { DisposableObject } from './pure/disposable-object';
import { commandRunner } from './commandRunner';
import { assertNever } from './pure/helpers-pure';
import { assertNever, ONE_HOUR_IN_MS, TWO_HOURS_IN_MS } from './pure/helpers-pure';
import { FullCompletedQueryInfo, FullQueryInfo, QueryStatus } from './query-results';
import { DatabaseManager } from './databases';
import { registerQueryHistoryScubber } from './query-history-scrubber';
@ -83,8 +83,15 @@ export enum SortOrder {
CountDesc = 'CountDesc',
}
const ONE_HOUR_IN_MS = 1000 * 60 * 60;
const TWO_HOURS_IN_MS = 1000 * 60 * 60 * 2;
/**
* Number of milliseconds two clicks have to arrive apart to be
* considered a double-click.
*/
const DOUBLE_CLICK_TIME = 500;
const NO_QUERY_SELECTED = 'No query selected. Select a query history item you have already run and try again.';
const WORKSPACE_QUERY_HISTORY_FILE = 'workspace-query-history.json';
/**
* Tree data provider for the query history view.
@ -244,15 +251,6 @@ export class HistoryTreeDataProvider extends DisposableObject {
}
}
/**
* Number of milliseconds two clicks have to arrive apart to be
* considered a double-click.
*/
const DOUBLE_CLICK_TIME = 500;
const NO_QUERY_SELECTED = 'No query selected. Select a query history item you have already run and try again.';
const WORKSPACE_QUERY_HISTORY_FILE = 'workspace-query-history.json';
export class QueryHistoryManager extends DisposableObject {
treeDataProvider: HistoryTreeDataProvider;
@ -742,7 +740,7 @@ export class QueryHistoryManager extends DisposableObject {
}
await this.tryOpenExternalFile(
await finalSingleItem.completedQuery.query.ensureCsvProduced(this.qs, this.dbm)
await finalSingleItem.completedQuery.query.ensureCsvAlerts(this.qs, this.dbm)
);
}

Просмотреть файл

@ -18,6 +18,14 @@ import { DatabaseInfo } from './pure/interface-types';
import { showAndLogErrorMessage } from './helpers';
import { asyncFilter } from './pure/helpers-pure';
/**
* query-results.ts
* ----------------
*
* A collection of classes and functions that collectively
* manage query results.
*/
/**
* A description of the information about a query
* that is available before results are populated.

Просмотреть файл

@ -1,10 +1,9 @@
import * as unzipper from 'unzipper';
import * as path from 'path';
import * as fs from 'fs-extra';
import { showAndLogWarningMessage } from '../helpers';
import { showAndLogWarningMessage, tmpDir } from '../helpers';
import { Credentials } from '../authentication';
import { logger } from '../logging';
import { tmpDir } from '../run-queries';
import { RemoteQueryWorkflowResult } from './remote-query-workflow-result';
import { DownloadLink } from './download-link';
import { RemoteQuery } from './remote-query';
@ -106,8 +105,8 @@ async function getResultIndexItems(
/**
* Gets the status of a workflow run.
* @param credentials Credentials for authenticating to the GitHub API.
* @param owner
* @param repo
* @param owner
* @param repo
* @param workflowRunId The ID of the workflow run to get the result index for.
* @returns The workflow run status.
*/

Просмотреть файл

@ -10,7 +10,6 @@ import {
} from 'vscode';
import * as path from 'path';
import { tmpDir } from '../run-queries';
import {
ToRemoteQueriesMessage,
FromRemoteQueriesMessage,
@ -25,7 +24,7 @@ import { AnalysisSummary, RemoteQueryResult } from './remote-query-result';
import { RemoteQuery } from './remote-query';
import { RemoteQueryResult as RemoteQueryResultViewModel } from './shared/remote-query-result';
import { AnalysisSummary as AnalysisResultViewModel } from './shared/remote-query-result';
import { showAndLogWarningMessage } from '../helpers';
import { showAndLogWarningMessage, tmpDir } from '../helpers';
import { URLSearchParams } from 'url';
import { SHOW_QUERY_TEXT_MSG } from '../query-history';
import { AnalysesResultsManager } from './analyses-results-manager';

Просмотреть файл

@ -10,13 +10,13 @@ import {
showAndLogErrorMessage,
showAndLogInformationMessage,
showInformationMessageWithAction,
tryGetQueryMetadata
tryGetQueryMetadata,
tmpDir
} from '../helpers';
import { Credentials } from '../authentication';
import * as cli from '../cli';
import { logger } from '../logging';
import { getRemoteControllerRepo, getRemoteRepositoryLists, setRemoteControllerRepo } from '../config';
import { tmpDir } from '../run-queries';
import { ProgressCallback, UserCancellationException } from '../commandRunner';
import { OctokitResponse } from '@octokit/types/dist-types';
import { RemoteQuery } from './remote-query';
@ -439,7 +439,7 @@ async function buildRemoteQueryEntity(
queryStartTime: Date,
workflowRunId: number
): Promise<RemoteQuery> {
// The query name is either the name as specified in the query metadata, or the file name.
// The query name is either the name as specified in the query metadata, or the file name.
const queryName = queryMetadata?.name ?? path.basename(queryFilePath);
const queryRepos = repositories.map(r => {

Просмотреть файл

@ -1,7 +1,7 @@
import * as crypto from 'crypto';
import * as fs from 'fs-extra';
import * as path from 'path';
import * as tmp from 'tmp-promise';
import * as path from 'path';
import { nanoid } from 'nanoid';
import {
CancellationToken,
@ -17,7 +17,7 @@ import { ErrorCodes, ResponseError } from 'vscode-languageclient';
import * as cli from './cli';
import * as config from './config';
import { DatabaseItem, DatabaseManager } from './databases';
import { getOnDiskWorkspaceFolders, showAndLogErrorMessage, tryGetQueryMetadata } from './helpers';
import { getOnDiskWorkspaceFolders, showAndLogErrorMessage, tryGetQueryMetadata, upgradesTmpDir } from './helpers';
import { ProgressCallback, UserCancellationException } from './commandRunner';
import { DatabaseInfo, QueryMetadata } from './pure/interface-types';
import { logger } from './logging';
@ -32,19 +32,20 @@ import { DecodedBqrsChunk } from './pure/bqrs-cli-types';
/**
* run-queries.ts
* -------------
* --------------
*
* Compiling and running QL queries.
*/
export const tmpDir = tmp.dirSync({ prefix: 'queries_', keep: false, unsafeCleanup: true });
export const upgradesTmpDir = tmp.dirSync({ dir: tmpDir.name, prefix: 'upgrades_', keep: false, unsafeCleanup: true });
export const tmpDirDisposal = {
dispose: () => {
upgradesTmpDir.removeCallback();
tmpDir.removeCallback();
}
};
/**
* Information about which query will be to be run. `quickEvalPosition` and `quickEvalText`
* is only filled in if the query is a quick query.
*/
interface SelectedQuery {
queryPath: string;
quickEvalPosition?: messages.Position;
quickEvalText?: string;
}
/**
* A collection of evaluation-time information about a query,
@ -131,6 +132,7 @@ export class QueryEvaluationInfo {
id: callbackId,
timeoutSecs: qs.config.timeoutSecs,
};
const dataset: messages.Dataset = {
dbDir: dbItem.contents.datasetUri.fsPath,
workingSet: 'default'
@ -235,6 +237,10 @@ export class QueryEvaluationInfo {
return fs.pathExists(this.csvPath);
}
/**
* Returns the path to the DIL file produced by this query. If the query has not yet produced DIL,
* this will return first create the DIL file and then return the path to the DIL file.
*/
async ensureDilPath(qs: qsClient.QueryServerClient): Promise<string> {
if (await this.hasDil()) {
return this.dilPath;
@ -250,6 +256,10 @@ export class QueryEvaluationInfo {
return this.dilPath;
}
/**
* Creates the CSV file containing the results of this query. This will only be called if the query
* does not have interpreted results and the CSV file does not already exist.
*/
async exportCsvResults(qs: qsClient.QueryServerClient, csvPath: string, onFinish: () => void): Promise<void> {
let stopDecoding = false;
const out = fs.createWriteStream(csvPath);
@ -266,14 +276,21 @@ export class QueryEvaluationInfo {
pageSize: 100,
offset: nextOffset,
});
for (const tuple of chunk.tuples)
for (const tuple of chunk.tuples) {
out.write(tuple.join(',') + '\n');
}
nextOffset = chunk.next;
}
out.end();
}
async ensureCsvProduced(qs: qsClient.QueryServerClient, dbm: DatabaseManager): Promise<string> {
/**
* Returns the path to the CSV alerts interpretation of this query results. If CSV results have
* not yet been produced, this will return first create the CSV results and then return the path.
*
* This method only works for queries with interpreted results.
*/
async ensureCsvAlerts(qs: qsClient.QueryServerClient, dbm: DatabaseManager): Promise<string> {
if (await this.hasCsv()) {
return this.csvPath;
}
@ -297,6 +314,9 @@ export class QueryEvaluationInfo {
return this.csvPath;
}
/**
* Cleans this query's results directory.
*/
async deleteQuery(): Promise<void> {
await fs.remove(this.querySaveDir);
}
@ -333,9 +353,7 @@ export async function clearCacheInDatabase(
}
/**
*
* @param filePath This needs to be equivalent to java Path.toRealPath(NO_FOLLOW_LINKS)
*
* @param filePath This needs to be equivalent to Java's `Path.toRealPath(NO_FOLLOW_LINKS)`
*/
async function convertToQlPath(filePath: string): Promise<string> {
if (process.platform === 'win32') {
@ -381,9 +399,9 @@ async function getSelectedPosition(editor: TextEditor, range?: Range): Promise<m
/**
* Compare the dbscheme implied by the query `query` and that of the current database.
* If they are compatible, do nothing.
* If they are incompatible but the database can be upgraded, suggest that upgrade.
* If they are incompatible and the database cannot be upgraded, throw an error.
* - If they are compatible, do nothing.
* - If they are incompatible but the database can be upgraded, suggest that upgrade.
* - If they are incompatible and the database cannot be upgraded, throw an error.
*/
async function checkDbschemeCompatibility(
cliServer: cli.CodeQLCliServer,
@ -431,7 +449,9 @@ async function checkDbschemeCompatibility(
}
function reportNoUpgradePath(qlProgram: messages.QlProgram, query: QueryEvaluationInfo): void {
throw new Error(`Query ${qlProgram.queryPath} expects database scheme ${query.queryDbscheme}, but the current database has a different scheme, and no database upgrades are available. The current database scheme may be newer than the CodeQL query libraries in your workspace.\n\nPlease try using a newer version of the query libraries.`);
throw new Error(
`Query ${qlProgram.queryPath} expects database scheme ${query.queryDbscheme}, but the current database has a different scheme, and no database upgrades are available. The current database scheme may be newer than the CodeQL query libraries in your workspace.\n\nPlease try using a newer version of the query libraries.`
);
}
/**
@ -476,7 +496,6 @@ async function compileNonDestructiveUpgrade(
qlProgram.dbschemePath = query.queryDbscheme;
// We are new enough that we will always support single file upgrades.
return result.compiledUpgrade;
}
/**
@ -521,12 +540,6 @@ async function promptUserToSaveChanges(document: TextDocument): Promise<boolean>
return false;
}
type SelectedQuery = {
queryPath: string;
quickEvalPosition?: messages.Position;
quickEvalText?: string;
};
/**
* Determines which QL file to run during an invocation of `Run Query` or `Quick Evaluation`, as follows:
* - If the command was called by clicking on a file, then use that file.
@ -537,12 +550,19 @@ type SelectedQuery = {
* @param selectedResourceUri The selected resource when the command was run.
* @param quickEval Whether the command being run is `Quick Evaluation`.
*/
export async function determineSelectedQuery(selectedResourceUri: Uri | undefined, quickEval: boolean, range?: Range): Promise<SelectedQuery> {
export async function determineSelectedQuery(
selectedResourceUri: Uri | undefined,
quickEval: boolean,
range?: Range
): Promise<SelectedQuery> {
const editor = window.activeTextEditor;
// Choose which QL file to use.
let queryUri: Uri;
if (selectedResourceUri === undefined) {
if (selectedResourceUri) {
// A resource was passed to the command handler, so use it.
queryUri = selectedResourceUri;
} else {
// No resource was passed to the command handler, so obtain it from the active editor.
// This usually happens when the command is called from the Command Palette.
if (editor === undefined) {
@ -550,9 +570,6 @@ export async function determineSelectedQuery(selectedResourceUri: Uri | undefine
} else {
queryUri = editor.document.uri;
}
} else {
// A resource was passed to the command handler, so use it.
queryUri = selectedResourceUri;
}
if (queryUri.scheme !== 'file') {
@ -680,7 +697,7 @@ export async function compileAndRunQueryAgainstDatabase(
try {
let upgradeQlo;
if (await hasNondestructiveUpgradeCapabilities(qs)) {
upgradeDir = await tmp.dir({ dir: upgradesTmpDir.name, unsafeCleanup: true });
upgradeDir = await tmp.dir({ dir: upgradesTmpDir, unsafeCleanup: true });
upgradeQlo = await compileNonDestructiveUpgrade(qs, upgradeDir, query, qlProgram, dbItem, progress, token);
} else {
await checkDbschemeCompatibility(cliServer, qs, query, qlProgram, dbItem, progress, token);
@ -746,6 +763,16 @@ export async function compileAndRunQueryAgainstDatabase(
}
}
/**
* Determines the initial information for a query. This is everything of interest
* we know about this query that is available before it is run.
*
* @param selectedQueryUri The Uri of the document containing the query to be run.
* @param databaseInfo The database to run the query against.
* @param isQuickEval true if this is a quick evaluation.
* @param range the selection range of the query to be run. Only used if isQuickEval is true.
* @returns The initial information for the query to be run.
*/
export async function createInitialQueryInfo(
selectedQueryUri: Uri | undefined,
databaseInfo: DatabaseInfo,
@ -776,12 +803,14 @@ const compilationFailedErrorTail = ' compilation failed. Please make sure there
' and the query and database use the same target language. For more details on the error, go to View > Output,' +
' and choose CodeQL Query Server from the dropdown.';
/**
* Create a synthetic result for a query that failed to compile.
*/
function createSyntheticResult(
query: QueryEvaluationInfo,
message: string,
resultType: number
): QueryWithResults {
return {
query,
result: {

Просмотреть файл

@ -1,10 +1,9 @@
import * as vscode from 'vscode';
import { getOnDiskWorkspaceFolders, showAndLogErrorMessage } from './helpers';
import { getOnDiskWorkspaceFolders, showAndLogErrorMessage, tmpDir } from './helpers';
import { ProgressCallback, UserCancellationException } from './commandRunner';
import { logger } from './logging';
import * as messages from './pure/messages';
import * as qsClient from './queryserver-client';
import { upgradesTmpDir } from './run-queries';
import * as tmp from 'tmp-promise';
import * as path from 'path';
import * as semver from 'semver';
@ -180,7 +179,7 @@ export async function upgradeDatabaseExplicit(
if (finalDbscheme === undefined) {
throw new Error('Could not determine target dbscheme to upgrade to.');
}
const currentUpgradeTmp = await tmp.dir({ dir: upgradesTmpDir.name, prefix: 'upgrade_', keep: false, unsafeCleanup: true });
const currentUpgradeTmp = await tmp.dir({ dir: tmpDir.name, prefix: 'upgrade_', keep: false, unsafeCleanup: true });
try {
let compileUpgradeResult: messages.CompileUpgradeResult;
try {

Просмотреть файл

@ -11,11 +11,12 @@ import { DatabaseItem, DatabaseManager } from '../../databases';
import { CodeQLExtensionInterface } from '../../extension';
import { dbLoc, storagePath } from './global.helper';
import { importArchiveDatabase } from '../../databaseFetcher';
import { compileAndRunQueryAgainstDatabase, createInitialQueryInfo, tmpDir } from '../../run-queries';
import { compileAndRunQueryAgainstDatabase, createInitialQueryInfo } from '../../run-queries';
import { CodeQLCliServer } from '../../cli';
import { QueryServerClient } from '../../queryserver-client';
import { skipIfNoCodeQL } from '../ensureCli';
import { QueryResultType } from '../../pure/messages';
import { tmpDir } from '../../helpers';
/**

Просмотреть файл

@ -8,21 +8,22 @@ import * as sinon from 'sinon';
import * as chaiAsPromised from 'chai-as-promised';
import { logger } from '../../logging';
import { QueryHistoryManager, HistoryTreeDataProvider, SortOrder } from '../../query-history';
import { registerQueryHistoryScubber } from '../../query-history-scrubber';
import { QueryEvaluationInfo, QueryWithResults, tmpDir } from '../../run-queries';
import { QueryHistoryManager, HistoryTreeDataProvider, SortOrder } from '../../query-history';
import { QueryEvaluationInfo, QueryWithResults } from '../../run-queries';
import { QueryHistoryConfigListener } from '../../config';
import * as messages from '../../pure/messages';
import { QueryServerClient } from '../../queryserver-client';
import { FullQueryInfo, InitialQueryInfo } from '../../query-results';
import { DatabaseManager } from '../../databases';
import * as tmp from 'tmp-promise';
import { ONE_DAY_IN_MS, ONE_HOUR_IN_MS, TWO_HOURS_IN_MS, THREE_HOURS_IN_MS } from '../../pure/helpers-pure';
import { tmpDir } from '../../helpers';
chai.use(chaiAsPromised);
const expect = chai.expect;
const assert = chai.assert;
describe('query-history', () => {
const mockExtensionLocation = path.join(tmpDir.name, 'mock-extension-location');
let configListener: QueryHistoryConfigListener;
@ -552,10 +553,6 @@ describe('query-history', () => {
let mockCtx: vscode.ExtensionContext;
let runCount = 0;
const ONE_HOUR_IN_MS = 60 * 60 * 1000;
const TWO_HOURS_IN_MS = 2 * ONE_HOUR_IN_MS;
const THREE_HOURS_IN_MS = 3 * ONE_HOUR_IN_MS;
const ONE_DAY_IN_MS = 24 * ONE_HOUR_IN_MS;
// We don't want our times to align exactly with the hour,
// so we can better mimic real life
const LESS_THAN_ONE_DAY = ONE_DAY_IN_MS - 1000;

Просмотреть файл

@ -6,12 +6,13 @@ import 'sinon-chai';
import * as sinon from 'sinon';
import * as chaiAsPromised from 'chai-as-promised';
import { FullQueryInfo, InitialQueryInfo, interpretResults } from '../../query-results';
import { QueryEvaluationInfo, QueryWithResults, tmpDir } from '../../run-queries';
import { QueryEvaluationInfo, QueryWithResults } from '../../run-queries';
import { QueryHistoryConfig } from '../../config';
import { EvaluationResult, QueryResultType } from '../../pure/messages';
import { DatabaseInfo, SortDirection, SortedResultSetInfo } from '../../pure/interface-types';
import { CodeQLCliServer, SourceInfo } from '../../cli';
import { CancellationTokenSource, Uri, env } from 'vscode';
import { tmpDir } from '../../helpers';
chai.use(chaiAsPromised);
const expect = chai.expect;