Skip to content

Release Notes — Sui 0.75Y

Branch: Sui-0.75Y
Base: Sui 0.75X (CSV compare, test report, query stats, export tracking)
Date: 2026-06-17


Summary of Changes

Area Type Description
Compare New feature CSV File Compare. New Tools → Compare CSV Files… wizard streams two CSV files of any size, computing full totals (matched / changed / left-only / right-only) while capturing only up to SUI.CSVDIFFMAX (default 20 000) differing rows. The captured sample is shown in the familiar DiffRep side-by-side view with the correct totals reflected in the summary and column summary
DiffRep New feature Test Report tab. A new Test report tab auto-generates a Markdown document covering: verdict (PASS / DIFFERENCES FOUND), sources, key columns, row summary, per-column summary, and the SQL used on each side. The report can be saved as .md or copied to the clipboard
DiffRep Enhancement Column summary → Open in query view. A new button on the Column summary tab opens the summary table as a full QueryRep result window (sortable, filterable, XLS-exportable)
DiffRep Enhancement SQL passthrough to report. When comparing two QueryRep windows directly (diffDirectFromQueryRep) or via Re-run on another connection, the SQL for each side is now forwarded to DiffRep and included in the test report
Main window Enhancement Query stats in status bar. The bottom status bar now shows the row count, status message, and duration of the most recently completed query run, alongside the existing cursor-position indicator
QueryBox Enhancement Run stats in history navigator. The info line in the QueryBox history navigator now includes the duration, row count, and status of the selected history entry in addition to the timestamp and connection URL
Query Monitor Enhancement Export file tracking. Each CSV and XLS export now records the output file path in the Query Monitor. A new Exported File column appears in the monitor window. QryMon also gains getSqlForExportFile() so CSV Compare can recover the originating SQL when the two files were exported from Sui in the same session
Query Monitor Enhancement Buffer increased 100 → 500. The ring buffer now holds the last 500 executed queries. The hardcoded 100 literal is replaced by a QryMon.MAX constant throughout
Compare / DiffRep Bug fix Closed tabbed result windows no longer appear in the diff picker. Tabbed QueryRep instances that were closed kept isTabbed = true and remained in openSelectWindows, surfacing as live candidates in the F7/F8 diff picker. A new QueryRep.unregister() method clears the flag and removes the entry; dead instances are also purged defensively during picker population
QueryBox Bug fix Null-pointer guard in QueryBox navigation. getSQLStmt() could return null for an uninitialized slot; calling .isEmpty() on it threw NullPointerException. Added an explicit null check before the test

Details

CSV File Compare (CsvCompare.java, Sui.java)

Accessible via Tools → Compare CSV Files…. The wizard prompts for two CSV files (defaulting to the configured export directory), then shows the common columns for key selection (or whole-row matching if no keys are ticked).

The comparison runs off the EDT behind a modal progress dialog (SwingWorker):

  • Phase 1 — the right file is indexed in memory as key → queue of rows.
  • Phase 2 — the left file is streamed; each row is looked up in the index and classified as matched, changed, or left-only.
  • Phase 3 — any rows left in the index are right-only.

Only differing rows (up to SUI.CSVDIFFMAX) are forwarded to DiffRep. DiffRep.setBaseIdenticalRows(n) is called with the full matched count so the summary, column summary, and test report all reflect the true file-wide totals — not just the captured sample.

The column metadata arrays sent to DiffRep are derived from the left-file header; right-file columns are remapped into left-column order via mapBtoA() so the side-by-side view aligns correctly even when the two files have different column orders.

Test Report tab (DiffRep.java, HelpDialog.java)

A new Test report tab is added next to Column summary in every DiffRep window. It contains:

  • A free-text Test description / notes area (persists while the window is open).
  • A rendered Markdown preview (reuses HelpDialog.renderPane()) that regenerates on focus-loss and via an explicit Refresh button.
  • Save .md… — saves the report as a UTF-8 Markdown file.
  • Copy Markdown — copies the raw Markdown to the system clipboard.

The generated document includes: title / verdict / key columns / generated timestamp, test description, left and right sources, a row-level summary table, a per-column summary table (identical/differ counts per column, accounting for baseIdenticalRows), and fenced SQL blocks for both sides (falls back to "SQL not available" when not supplied).

Query stats in main status bar (Sui.java)

A lastRunLabel is added to the south status panel alongside the cursor-position indicator. After every query completes, updateLastRunLabel(durMs, rows, status) formats and pushes a string such as 1 234 rows | OK | 450 ms. It fires both from the history-update path (when the query is found in QryList) and from the unconditional fall-through path for queries that are not tracked in history.

Export file tracking (QryMon.java, ExpCSV.java, ExpXLS.java, ExpXLSRS.java, QueryRep.java, RunIt.java)

Each export class now exposes a savedPath field (set after a successful write). Call sites in QueryRep and RunIt capture the field and call QryMon.setExpFileByStart(start, path). Multiple exports from the same slot accumulate as a |-separated string in QryMon.expFile[].

QryMon.getSqlForExportFile(path) walks the ring buffer to find the slot whose expFile contains the given path (exact match, then bare filename fallback) and returns the originating SQL. CsvCompare calls this to populate the SQL blocks in the test report.

Closed tabbed windows in diff picker (QueryRep.java, QueryRepTabbedFrame.java)

QueryRepTabbedFrame.disposeTab(QueryRep) disposes the frame but bypasses the regular closeRep() cleanup, leaving the QueryRep in openSelectWindows with isTabbed = true. The new QueryRep.unregister(qr) method removes the instance from the list and clears the flag; disposeTab now calls it. The diff-picker collection loop also defensively removes any entry where !qr.isDisplayable() && !QueryRepTabbedFrame.getInstance().containsQR(qr).


Files Changed

File Change
src/CsvCompare.java New — CSV streaming compare engine, key-column dialog, DiffRep integration
src/DiffRep.java Test report tab; setBaseIdenticalRows(); Column-summary Open in query view; initialKeyCols + leftSql/rightSql constructor params; baseIdenticalRows accounted in summary and column summary
src/HelpDialog.java New renderPane(Window, String) static method for embedding rendered Markdown in other windows
src/QryMon.java MAX = 500; expFile[] array; findIdxByStart(), setExpFile(), setExpFileByStart(), getSqlForExportFile(); Exported File column in monitor window; array-size fix (cT/cTn/cLen/cL now sized to column count, not row count)
src/ExpCSV.java savedPath field
src/ExpXLS.java savedPath field
src/ExpXLSRS.java savedPath field
src/QueryRep.java Export actions capture savedPath → QryMon; unregister(); dead-entry purge in diff-picker; lSql/rSql passed to DiffRep on direct diff
src/RunIt.java Export file paths recorded in QryMon for XLS and CSV background exports
src/RunItDiff.java leftSql constructor parameter forwarded to DiffRep
src/Sui.java Compare CSV Files… menu item; lastRunLabel status bar; updateLastRunLabel(); QryMon.MAX constant used in ConcQueries
src/ShowQryBox.java buildInfoLine() adds duration/rows/status to history navigator; null guard on getSQLStmt() result
src/QueryRepTabbedFrame.java containsQR() check; calls QueryRep.unregister() on tab dispose