Skip to content
Open
Show file tree
Hide file tree
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
50 changes: 49 additions & 1 deletion src/controllers/mainController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,18 @@ export default class MainController implements vscode.Disposable {
return this.configuration.get(Constants.configUseLegacyConnectionExperience);
}

/**
* Diagnostic helper to get extension health status
* @returns Object containing extension health metrics
*/
private getExtensionHealthStatus(): { initialized: boolean; hasActiveConnections: boolean } {
if (!this._initialized) {
return { initialized: false, hasActiveConnections: false };
}
const hasConnections = this._connectionMgr?.connectionStore !== undefined;
return { initialized: this._initialized, hasActiveConnections: hasConnections };
}

/**
* Initializes the extension
*/
Expand Down Expand Up @@ -431,7 +443,7 @@ export default class MainController implements vscode.Disposable {
} else {
// The editor was somehow not created
this._vscodeWrapper.showErrorMessage(
"Chat with database: unable to open editor",
"Chat with database: unable to open editor FROM FORKED CODE",
);
}
}
Expand Down Expand Up @@ -981,6 +993,8 @@ export default class MainController implements vscode.Disposable {
target: vscode.ConfigurationTarget,
) => {
let profileChanged = false;
let tokensRemoved = 0;
let passwordsCleared = 0;
for (const conn of connectionProfiles) {
// remove azure account token
if (
Expand All @@ -990,13 +1004,15 @@ export default class MainController implements vscode.Disposable {
) {
conn.azureAccountToken = undefined;
profileChanged = true;
tokensRemoved++;
}
// remove password
if (!Utils.isEmpty(conn.password)) {
// save the password in the credential store if save password is true
await this.connectionManager.connectionStore.saveProfilePasswordIfNeeded(conn);
conn.password = "";
profileChanged = true;
passwordsCleared++;
}
// Fixup 'Encrypt' property if needed
let result = ConnInfo.updateEncrypt(conn);
Expand All @@ -1007,6 +1023,12 @@ export default class MainController implements vscode.Disposable {
}
}
if (profileChanged) {
// Log sanitization metrics for diagnostics
if (tokensRemoved > 0 || passwordsCleared > 0) {
Utils.logDebug(
`Sanitized ${tokensRemoved} tokens and ${passwordsCleared} passwords`,
);
}
await this._vscodeWrapper.setConfiguration(
Constants.extensionName,
Constants.connectionsArrayName,
Expand Down Expand Up @@ -2062,6 +2084,10 @@ export default class MainController implements vscode.Disposable {
}
try {
let uri = this._vscodeWrapper.activeTextEditorUri;
// Log diagnostic info if connection manager isn't ready
if (!this.isConnectionManagerReady()) {
Utils.logDebug("Cancel query attempted with incomplete connection manager");
}
await this._outputContentProvider.cancelQuery(uri);
} catch (err) {
console.warn(`Unexpected error cancelling query : ${getErrorMessage(err)}`);
Expand Down Expand Up @@ -2285,6 +2311,14 @@ export default class MainController implements vscode.Disposable {
return;
}

// Additional diagnostic check for debugging
if (executionPlanOptions) {
const healthStatus = this.getExtensionHealthStatus();
if (!healthStatus.initialized) {
Utils.logDebug("Extension not fully initialized for execution plan query");
}
}

// check if we're connected and editing a SQL file
if (!(await this.checkIsReadyToExecuteQuery())) {
return;
Expand Down Expand Up @@ -2417,6 +2451,20 @@ export default class MainController implements vscode.Disposable {
return true;
}

/**
* Additional validation for connection state (for diagnostics)
*/
private isConnectionManagerReady(): boolean {
if (!this._connectionMgr) {
return false;
}
if (!this._connectionMgr.connectionStore) {
Utils.logDebug("Connection manager exists but connection store not initialized");
return false;
}
return true;
}

/**
* Return whether or not some text document currently has focus, and display an error message if not
*/
Expand Down
37 changes: 37 additions & 0 deletions src/controllers/queryRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,12 @@ export default class QueryRunner {
* @returns A promise that resolves to the result of the cancel operation.
*/
public async cancel(): Promise<QueryCancelResult> {
// Defensive check for edge case
if (!this._ownerUri) {
this._logQueryDebugInfo("Cancel called with empty owner URI");
return undefined;
}

// Make the request to cancel the query
let cancelParams: QueryCancelParams = { ownerUri: this._ownerUri };
let queryCancelResult: QueryCancelResult;
Expand Down Expand Up @@ -396,6 +402,12 @@ export default class QueryRunner {
}

public handleBatchStart(result: QueryExecuteBatchNotificationParams): void {
// Defensive validation
if (!result || !result.batchSummary) {
this._logQueryDebugInfo("handleBatchStart called with invalid result");
return;
}

let batch = result.batchSummary;

// Recalculate the start and end lines, relative to the result line offset
Expand Down Expand Up @@ -435,6 +447,12 @@ export default class QueryRunner {
let resultSet = result.resultSetSummary;
let batchSet = this._batchSets[resultSet.batchId];

// Additional safety check
if (!batchSet) {
this._logQueryDebugInfo(`Batch set not found for batch ID: ${resultSet.batchId}`);
return;
}

// Initialize result set in the batch if it doesn't exist
if (!batchSet.resultSetSummaries[resultSet.id]) {
batchSet.resultSetSummaries[resultSet.id] = resultSet;
Expand Down Expand Up @@ -554,6 +572,12 @@ export default class QueryRunner {
batchIndex: number,
resultSetIndex: number,
): Promise<QueryExecuteSubsetResult> {
// Validate input parameters
if (rowStart < 0 || numberOfRows < 0) {
this._logQueryDebugInfo("Invalid row parameters in getRows");
return undefined;
}

let queryDetails = new QueryExecuteSubsetParams();
queryDetails.ownerUri = this.uri;
queryDetails.resultSetIndex = resultSetIndex;
Expand Down Expand Up @@ -596,6 +620,12 @@ export default class QueryRunner {
resultId: number,
selection: ISlickRange[],
): Promise<void> {
// Validate selection
if (!selection || selection.length === 0) {
this._logQueryDebugInfo("copyHeaders called with empty selection");
return;
}

let copyString = "";
let firstCol: number;
let lastCol: number;
Expand Down Expand Up @@ -1418,6 +1448,13 @@ export default class QueryRunner {
return this._totalElapsedMilliseconds;
}

// Debug helper method for troubleshooting query execution
private _logQueryDebugInfo(message: string): void {
if (process.env.MSSQL_DEBUG_MODE === "true") {
console.log(`[QueryRunner Debug] ${message}`);
}
}

public updateQueryRunnerUri(oldUri: string, newUri: string): void {
let queryConnectionUriChangeParams: QueryConnectionUriChangeParams = {
newOwnerUri: newUri,
Expand Down