Skip to content
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

Added Query Plan to results pane #18124

Open
wants to merge 19 commits into
base: main
Choose a base branch
from

Conversation

laurennat
Copy link
Contributor

queryPlanInResultsPane

Copy link

github-actions bot commented Oct 1, 2024

VSIX Size Comparison

  • Main branch VSIX size: 13192 KB
  • PR branch VSIX size: 13196 KB
  • Size difference: $${\color{green} 4 KB \space (0\%) }$$

React Webview Bundle Size Comparison

  • Main branch bundle size: 2944 KB
  • PR branch bundle size: 2956 KB
  • Size difference: $${\color{green} 12 KB \space (0\%) }$$

src/models/sqlOutputContentProvider.ts Outdated Show resolved Hide resolved
src/models/sqlOutputContentProvider.ts Outdated Show resolved Hide resolved
src/models/sqlOutputContentProvider.ts Show resolved Hide resolved
src/queryResult/queryResultWebViewController.ts Outdated Show resolved Hide resolved
@caohai
Copy link
Member

caohai commented Oct 2, 2024

I noticed in the GIF that the column name under Results tab for query plan has a TODO tag and is not localized, I've included that fix in my PR here fa292ac

@caohai
Copy link
Member

caohai commented Oct 3, 2024

Can you add/update the screenshot/gif with a multi-batch query like this?

select * from sys.objects
go
select * from sys.objects
go
Screenshot 2024-10-03 at 14 26 58

Here's what I'm seeing with your branch, looks like the query plan for the first query is being displayed, you can address multi-query support in later PRs.

One more thing I noticed: In ADS if you click the link for query plan xml in the result, a query plan editor tab will be opened, we can look into if we want to support this in vscode with the links. Currently clicking them doesn't do anything (as they are not wired up), feel free to ping me for the link handler if you need more info.
image

const classes = useStyles();
const provider = useContext(ExecutionPlanContext);
const loadState = provider?.state?.loadState ?? ApiStatus.Loading;
console.log(context);
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 want to keep this log for prod or is it just for debugging?

@@ -11,11 +18,43 @@ export enum QueryResultLoadState {

export interface QueryResultReactProvider {
setResultTab: (tabId: QueryResultPaneTabs) => void;
/**
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe extract it out to a parent interface shared between the 2 webviews.
(execution plan and query result)

@@ -27,12 +66,37 @@ export interface QueryResultWebviewState {
resultSetSummaries: { [key: number]: ResultSetSummary };
messages: IMessage[];
tabStates?: QueryResultTabStates;
isExecutionPlan?: boolean;
executionPlanState: {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not make an interface out of it and share it with the regular plan webview. Let's try avoid duplicate types since they tend to get out of sync with each other.

sqlPlanContent?: string;
executionPlan?: GetExecutionPlanResult;
executionPlanGraphs?: ExecutionPlanGraph[];
theme?: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

Theming variable is provided by vscodeWebviewProvider. Please try to use that instead. Things like theming, telemetry and localization should be kept centralized as any bug fixes made to them will be applied everywhere.

}

export interface QueryResultReducers {
setResultTab: {
tabId: QueryResultPaneTabs;
};
getExecutionPlan: {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please centralize the reducers as well

@@ -41,7 +45,50 @@ const QueryResultStateProvider: React.FC<QueryResultContextProps> = ({
tabId: tabId,
});
},
getExecutionPlan: function (
Copy link
Contributor

Choose a reason for hiding this comment

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

If these are copied from the execution plan provider, it will be good to have one implementation of it.

@@ -85,6 +87,7 @@ const RESULTS = l10n.t("Results");
const MESSAGES = l10n.t("Messages");
const TIMESTAMP = l10n.t("Timestamp");
const MESSAGE = l10n.t("Message");
const QUERY_PLAN = l10n.t("Query Plan");
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe use the locFile in reactviews/common/locConstants.ts

Copy link
Contributor

Choose a reason for hiding this comment

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

@caohai and @laurennat, FYI, this approach won’t work because the webview initially loads with default strings. It then requests the extension to retrieve the current language file and updates the strings accordingly. Since these are constant variables, they cannot be updated dynamically after initialization.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks @aasimkhan30 ! I created a PR fixing this #18136

totalCost: 0,
executionPlanState: {
sqlPlanContent: executionPlanContents,
theme:
Copy link
Contributor

Choose a reason for hiding this comment

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

Please use the theme variable in vscodeWebviewContextProvider. This implementation already doesn't support high contrast theme.

};
});
}

private async createExecutionPlanGraphs() {
if (!this.state.executionPlan) {
if (!this.state.executionPlanState.executionPlan) {
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: Why not make this function cleaner and just create a new variable and add all the properties and the end

var executionPlan = {
	//fill all the required stuff
}
this.state = {
	this.state,
	executionPlanState: executionPlan
}

This will prevent you from repeating this.state.executionPlanState... every time.

className={classes.queryResultContainer}
style={{ height: "100%", minHeight: "300px" }}
>
<ExecutionPlanPage context={state} />
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think passing context in this way is the best design approach. It looks like you've special-cased the context variable in all the ExecutionPlan children components:

var context = context ?? useContext(ExecutionPlanProvider)

The purpose of using context is to avoid prop drilling (i.e., passing props down through multiple child components). A more effective way to handle this would be:

<ExecutionPlanContextProvider
   props={queryResultState.executionPlan}
>
   <ExecutionPlanPage />
</ExecutionPlanContextProvider>

By properly initializing all the props in the provider, you wouldn't need to modify the existing ExecutionPlan components.

@@ -104,7 +109,7 @@ export class ExecutionPlanWebviewController extends ReactWebviewPanelController<

if (saveUri) {
// Write the content to the new file
await vscode.workspace.fs.writeFile(
vscode.workspace.fs.writeFile(
Copy link
Contributor

Choose a reason for hiding this comment

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

if writeFile is async and we don't want to await it, then we should explicitly use the void keyword.

@@ -118,49 +123,58 @@ export class ExecutionPlanWebviewController extends ReactWebviewPanelController<
language: "xml",
});

await vscode.window.showTextDocument(planXmlDoc);
vscode.window.showTextDocument(planXmlDoc);
Copy link
Contributor

Choose a reason for hiding this comment

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

void keyword


return state;
});
this.registerReducer("showQuery", async (state, payload) => {
await this.untitledSqlDocumentService.newQuery(payload.query);
this.untitledSqlDocumentService.newQuery(payload.query);
Copy link
Contributor

Choose a reason for hiding this comment

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

void keyword

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants