6.3 KiB
6.3 KiB
Features
- Is a part of the SingleDatabaseWindow
- It's view should go in QueryToolView.axaml
- Uses mvvm
- AvaloniaEdit should be used as a query editor
Editor
- Use CodeEditorView
- We want to be able to open and save .sql files with the editor
Result grid
- Use an Avalonia.Controls.DataGrid
- The columns will change on runtime so it should be able to get the column count captions and data types from the viewmodel
- We want to be able to sort the columns
- We want to be able to filter the rows by defining conditions for the columns
- We want to be able to copy the rows to the clipboard
- We want to be able to customize cell rendering to use different colors for types and also do special things like rendering booleans as green checks and red crosses
- Be aware results may contain many rows, we should make a decision on how to handle this
- We want to be able to save the results to a file
Step-by-step plan to create the Query Tool
-
Add QueryToolView to the UI shell.
- Place the view in pgLabII\Views\QueryToolView.axaml and include it within SingleDatabaseWindow as a child region/panel. Ensure DataContext is set to QueryToolViewModel.
- Confirm MVVM wiring: commands and properties will be bound from the ViewModel.
-
Integrate the SQL editor.
- Embed AvaloniaEdit editor in the top area of QueryToolView.
- Bind editor text to a ViewModel property (e.g., UserSql).
- Provide commands for OpenSqlFile and SaveSqlFile; wire to toolbar/buttons and standard shortcuts (Ctrl+O/Ctrl+S).
- Ensure file filters default to .sql and that encoding/line-endings preserve content when saving.
-
Add a results toolbar for query operations.
- Buttons/controls: Run, Cancel (optional), "Load more", Auto-load on scroll toggle, Export..., and a compact status/summary text (e.g., "Showing X of Y rows").
- Bind to RunQuery, LoadMore, ExportResults, AutoLoadMore, ResultSummary, and Status properties.
-
Add the result grid using Avalonia.Controls.DataGrid.
- Enable row and column virtualization. Keep cell templates lightweight to preserve performance.
- Start with AutoGenerateColumns=true; later switch to explicit columns if custom cell templates per type are needed.
- Bind Items to a read-only observable collection of row objects (e.g., Rows).
- Enable extended selection and clipboard copy.
-
Support dynamic columns and types from the ViewModel.
- Expose a Columns metadata collection (names, data types, display hints) from the ViewModel.
- On first page load, update metadata so the grid can reflect the current query’s shape.
- If AutoGenerateColumns is disabled, construct DataGrid columns based on metadata (text, number, date, boolean with check/cross visuals).
-
Sorting model.
- On column header sort request, send sort descriptor(s) to the ViewModel.
- Re-run the query via server-side ORDER BY by wrapping the user SQL as a subquery and applying sort expressions.
- Reset paging when sort changes (reload from page 1).
- Clearly indicate if sorting is client-side (fallback) and only affects loaded rows.
-
Filtering model.
- Provide a simple filter row/panel to define per-column conditions.
- Convert user-entered filters to a filter descriptor list in the ViewModel.
- Prefer server-side WHERE by wrapping the user SQL; reset paging when filters change.
- If server-side wrapping is not possible for a given statement, apply client-side filtering to the currently loaded subset and warn that the filter is partial.
-
Data paging and virtualization (for 100k+ rows).
- Choose a default page size of 1000 rows (range 500–2000).
- On RunQuery: clear rows, reset page index, set CanLoadMore=true, fetch page 1.
- "Load more" fetches the next page and appends. Enable infinite scroll optionally when near the end.
- Display summary text: "Showing N of M+ rows" when total is known; otherwise "Showing N rows".
- Consider a cap on retained rows (e.g., last 10–20k) if memory is a concern.
-
Query execution abstraction.
- Use a service (e.g., IQueryExecutor) to run database calls.
- Provide: FetchPageAsync(userSql, sort, filters, page, size, ct) and StreamAllAsync(userSql, sort, filters, ct) for export.
- Wrap user SQL as a subquery to inject WHERE/ORDER BY/LIMIT/OFFSET safely; trim trailing semicolons.
- Prefer keyset pagination when a stable ordered key exists.
-
Export/Save results.
- Export should re-execute the query and stream the full result set directly from the database to CSV/TSV/JSON.
- Do not export from the grid items because the grid may contain only a subset of rows.
- Provide a Save As dialog with format choice and destination path.
- Copy to clipboard and selection.
- Enable extended row selection in the grid; support Ctrl+C to copy selected rows.
- Provide a toolbar "Copy" button as an alternative entry point.
- Status, cancellation, and errors.
- Show progress/state (Running, Idle, Loading page k, Cancelled, Error).
- Support cancellation tokens for long-running queries and paging operations.
- Surface exceptions as non-blocking notifications and preserve the last successful rows.
- Theming and custom cell rendering.
- Apply subtle coloring by type (numbers, dates, strings) via cell styles or templates.
- Render booleans as green checks/red crosses with minimal template overhead to keep virtualization effective.
- Wiring in SingleDatabaseWindow.
- Add a dedicated region/tab/panel for the Query Tool.
- Ensure lifetime management of the QueryToolViewModel aligns with the connection/session scope.
- Provide the active connection context/service to the ViewModel (DI or constructor).
- Testing and verification.
- Manual test: small query, large query (100k rows), sorting, filtering, load more, infinite scroll, export, copy, boolean rendering.
- Edge cases: empty results, wide tables (many columns), slow network, cancellation mid-page, schema change between pages.
- Performance check: scroll smoothness, memory growth under repeated paging, export throughput.
- Documentation and UX notes.
- In help/tooltip, clarify that sorting/filtering are server-side when possible; otherwise they apply only to loaded rows.
- Show a banner when results are truncated by paging limits and how to load more.