-
Notifications
You must be signed in to change notification settings - Fork 456
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
base: main
Are you sure you want to change the base?
Conversation
laurennat
commented
Oct 1, 2024
VSIX Size Comparison
React Webview Bundle Size Comparison
|
I noticed in the GIF that the column name under |
const classes = useStyles(); | ||
const provider = useContext(ExecutionPlanContext); | ||
const loadState = provider?.state?.loadState ?? ApiStatus.Loading; | ||
console.log(context); |
There was a problem hiding this comment.
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; | |||
/** |
There was a problem hiding this comment.
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: { |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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: { |
There was a problem hiding this comment.
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 ( |
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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} /> |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
void keyword