Skip to content

Reset flaggedForDeletion property after query editor close & reopen #19407

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions localization/l10n/bundle.l10n.json
Original file line number Diff line number Diff line change
@@ -696,6 +696,7 @@
"Azure Code Grant": "Azure Code Grant",
"Azure Device Code": "Azure Device Code",
"Azure Logs": "Azure Logs",
"Query Results": "Query Results",
"Open": "Open",
"Ignore Tenant": "Ignore Tenant",
"Your tenant '{0} ({1})' requires you to re-authenticate again to access {2} resources. Press Open to start the authentication process./{0} is the tenant name{1} is the tenant id{2} is the resource": {
3 changes: 3 additions & 0 deletions localization/xliff/vscode-mssql.xlf
Original file line number Diff line number Diff line change
@@ -1411,6 +1411,9 @@
<trans-unit id="++CODE++f44ad102b5dd5df1b5691408d19d39ee92cc1c9ad20f7125845df3d961a805d3">
<source xml:lang="en">Query Plan</source>
</trans-unit>
<trans-unit id="++CODE++70e130053d5cc32db21828d00d7074434b6f8fbabb7fb0169014d2ee1714b5af">
<source xml:lang="en">Query Results</source>
</trans-unit>
<trans-unit id="++CODE++9e83dc6d2db272ac27cc875b994f834b62b7202a49b69e885f8f4e8f9c29a452">
<source xml:lang="en">Query executed</source>
</trans-unit>
1 change: 1 addition & 0 deletions src/constants/locConstants.ts
Original file line number Diff line number Diff line change
@@ -118,6 +118,7 @@ export let authTypeAzureActiveDirectory = l10n.t("Microsoft Entra Id - Universal
export let azureAuthTypeCodeGrant = l10n.t("Azure Code Grant");
export let azureAuthTypeDeviceCode = l10n.t("Azure Device Code");
export let azureLogChannelName = l10n.t("Azure Logs");
export let queryResultChannelName = l10n.t("Query Results");
export let azureConsentDialogOpen = l10n.t("Open");
export let azureConsentDialogIgnore = l10n.t("Ignore Tenant");
export function azureConsentDialogBody(tenantName: string, tenantId: string, resource: string) {
14 changes: 14 additions & 0 deletions src/controllers/queryRunner.ts
Original file line number Diff line number Diff line change
@@ -45,6 +45,7 @@ import * as os from "os";
import { Deferred } from "../protocol";
import { sendActionEvent } from "../telemetry/telemetry";
import { TelemetryActions, TelemetryViews } from "../sharedInterfaces/telemetry";
import { Logger } from "../models/logger";

export interface IResultSet {
columns: string[];
@@ -68,6 +69,7 @@ export default class QueryRunner {
private _uriToQueryPromiseMap = new Map<string, Deferred<boolean>>();
private _uriToQueryStringMap = new Map<string, string>();
private static _runningQueries = [];
protected logger: Logger;

// CONSTRUCTOR /////////////////////////////////////////////////////////

@@ -91,6 +93,13 @@ export default class QueryRunner {
this._vscodeWrapper = new VscodeWrapper();
}

// Setup Logger

let channel = this._vscodeWrapper.createOutputChannel(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need a separate logging channel? I'm wonder if it's best practice to create new output channels per feature area?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just following the convention used elsewhere in the extension, should we consolidate all the logs into one channel?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be using vscodeWrapper.outputChannel, which will reuse the existing MSSQL one. Also, ideally adding a logging prefix to make logs easier to filter through: Logger.create(channel, "Querys") or maybe "QueryRunner"

LocalizedConstants.queryResultChannelName,
);
this.logger = Logger.create(channel);

// Store the state
this._isExecuting = false;
this._totalElapsedMilliseconds = 0;
@@ -150,6 +159,7 @@ export default class QueryRunner {
// PUBLIC METHODS ======================================================

public async cancel(): Promise<QueryCancelResult> {
this.logger.verbose("Send request to cancel query for URI: " + this._ownerUri);
// Make the request to cancel the query
let cancelParams: QueryCancelParams = { ownerUri: this._ownerUri };
let queryCancelResult = await this._client.sendRequest(
@@ -175,6 +185,7 @@ export default class QueryRunner {
line: line,
column: column,
};
this.logger.verbose("Send request to execute query for URI: " + this._ownerUri);

// Send the request to execute the query
await this._client
@@ -190,6 +201,7 @@ export default class QueryRunner {
executionPlanOptions?: ExecutionPlanOptions,
promise?: Deferred<boolean>,
): Promise<void> {
this.logger.verbose(`Starting to run query for URI: ${this._ownerUri}`);
await this.doRunQuery(selection, async (onSuccess, onError) => {
// Put together the request
let queryDetails: QueryExecuteParams = {
@@ -239,6 +251,8 @@ export default class QueryRunner {

let onSuccess = (result) => {
// The query has started, so lets fire up the result pane
this.logger.verbose(`Query started successfully for URI: ${this._ownerUri}`);

QueryRunner._runningQueries.push(vscode.Uri.parse(this._ownerUri).fsPath);
vscode.commands.executeCommand(
"setContext",
38 changes: 35 additions & 3 deletions src/models/sqlOutputContentProvider.ts
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ import { sendActionEvent } from "../telemetry/telemetry";
import { QueryResultWebviewController } from "../queryResult/queryResultWebViewController";
import { IMessage, QueryResultPaneTabs } from "../sharedInterfaces/queryResult";
import { TelemetryActions, TelemetryViews } from "../sharedInterfaces/telemetry";
import { Logger } from "./logger";
// tslint:disable-next-line:no-require-imports
const pd = require("pretty-data").pd;

@@ -48,6 +49,7 @@ export class SqlOutputContentProvider {
private _queryResultWebviewController: QueryResultWebviewController;
private _executionPlanOptions: ExecutionPlanOptions = {};
private _lastSendMessageTime: number;
protected logger: Logger;

// CONSTRUCTOR /////////////////////////////////////////////////////////
constructor(
@@ -58,6 +60,12 @@ export class SqlOutputContentProvider {
if (!_vscodeWrapper) {
this._vscodeWrapper = new VscodeWrapper();
}
// Setup Logger

let channel = this._vscodeWrapper.createOutputChannel(
LocalizedConstants.queryResultChannelName,
);
this.logger = Logger.create(channel);
}

public setQueryResultWebviewController(
@@ -334,9 +342,16 @@ export class SqlOutputContentProvider {
public createQueryRunner(statusView: StatusView, uri: string, title: string): QueryRunner {
// Reuse existing query runner if it exists
let queryRunner: QueryRunner;
let queryRunnerState: QueryRunnerState;

if (this._queryResultsMap.has(uri)) {
let existingRunner: QueryRunner = this._queryResultsMap.get(uri).queryRunner;
this.logger.verbose(`Reusing query runner for URI: ${uri}`);
queryRunnerState = this._queryResultsMap.get(uri);
// If the query runner is flagged for deletion, we need to reset it before reusing
if (queryRunnerState.flaggedForDeletion) {
queryRunnerState.flaggedForDeletion = false;
}
let existingRunner: QueryRunner = queryRunnerState.queryRunner;

// If the query is already in progress, don't attempt to send it
if (existingRunner.isExecutingQuery) {
@@ -352,12 +367,16 @@ export class SqlOutputContentProvider {
} else {
// We do not have a query runner for this editor, so create a new one
// and map it to the results uri
this.logger.verbose(`Creating a new query runner for URI: ${uri}`);
queryRunner = new QueryRunner(uri, title, statusView ? statusView : this._statusView);
queryRunner.eventEmitter.on("start", async (panelUri) => {
if (this.useLegacyQueryResults) {
this._panels.get(queryRunner.uri).proxy.sendEvent("start", panelUri);
sendActionEvent(TelemetryViews.ResultsGrid, TelemetryActions.OpenQueryResult);
} else {
this.logger.verbose(
`Creating a new query result webview for URI: ${queryRunner.uri}`,
);
this._lastSendMessageTime = Date.now();
this._queryResultWebviewController.addQueryResultState(
queryRunner.uri,
@@ -396,6 +415,9 @@ export class SqlOutputContentProvider {
}
});
queryRunner.eventEmitter.on("batchStart", async (batch) => {
this.logger.verbose(
`Batch start event received for URI: ${queryRunner.uri} with batchId: ${batch.batchId}`,
);
let time = new Date().toLocaleTimeString();
if (batch.executionElapsed && batch.executionEnd) {
time = new Date(batch.executionStart).toLocaleTimeString();
@@ -458,6 +480,9 @@ export class SqlOutputContentProvider {
queryRunner.eventEmitter.on(
"complete",
async (totalMilliseconds, hasError, isRefresh?) => {
this.logger.verbose(
`Query complete event received for URI: ${queryRunner.uri}`,
);
if (!isRefresh) {
// only update query history with new queries
this._vscodeWrapper.executeCommand(
@@ -513,7 +538,7 @@ export class SqlOutputContentProvider {
public cancelQuery(input: QueryRunner | string): void {
let self = this;
let queryRunner: QueryRunner;

this.logger.verbose(`Canceling query for URI: ${input}`);
if (typeof input === "string") {
if (this._queryResultsMap.has(input)) {
// Option 1: The string is a results URI (the results tab has focus)
@@ -533,8 +558,9 @@ export class SqlOutputContentProvider {

// Cancel the query
queryRunner.cancel().then(
(success) => undefined,
(success) => this.logger.verbose(`Query cancel success: ${success}`),
(error) => {
this.logger.verbose(`Query cancel error: ${error.message}`);
// On error, show error message
self._vscodeWrapper.showErrorMessage(
LocalizedConstants.msgCancelQueryFailed(error.message),
@@ -622,9 +648,15 @@ export class SqlOutputContentProvider {

if (queryRunnerState.queryRunner.isExecutingQuery) {
// We need to cancel it, which will dispose it
this.logger.verbose(
`Canceling query for URI: ${uri} as it is flagged for deletion.`,
);
this.cancelQuery(queryRunnerState.queryRunner);
} else {
// We need to explicitly dispose the query
this.logger.verbose(
`Disposing query for URI: ${uri} as it is flagged for deletion.`,
);
void queryRunnerState.queryRunner.dispose();
}
} else {
Loading
Oops, something went wrong.