Commit Graph
10 Commits
Author SHA1 Message Date
nngoandClaude Sonnet 4.6 9f0891755e feat: multi-result tabs, column statistics, and Ctrl+W close tab
- ResultsPanel rewritten: single queries show one result as before;
  scripts with multiple statements show each result in a named sub-tab
  (e.g. "Result 2 (1,234)") with the tab bar auto-shown/hidden
- EditorTab._on_script_done now calls show_script_results() instead of
  overwriting the panel on each statement
- ColumnStatsDialog: right-click any column header in TableViewer to see
  total rows, null count, distinct values, min, max, and avg (async,
  gracefully skips avg for non-numeric columns)
- TableViewer header context menu also adds "Resize to fit" shortcuts
- Ctrl+W closes the current workspace tab (View menu)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 17:10:35 -04:00
nngoandClaude Sonnet 4.6 771c92b7b6 feat: preferences dialog, SQL formatter, and enhanced table context menu
- PreferencesDialog (Tools → Preferences, Ctrl+,): font family/size,
  word wrap, default page size, query timeout, max history, auto-commit;
  font changes apply live to all open editors via apply_settings() cascade
- CodeEditor and TableViewer now read initial values from settings singleton
- SQL formatter (≡ Format button): uses sqlparse to reindent and uppercase
  keywords; gracefully prompts to install sqlparse if missing
- Table viewer context menu: "Filter by this value" (populates WHERE and
  reloads), "Copy row as SQL INSERT" (ready-to-paste INSERT statements),
  renamed "Copy row" → "Copy row as TSV" for clarity

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 17:05:54 -04:00
nngoandClaude Sonnet 4.6 972166e62a feat: shortcuts dialog, tab context menus, smart status bar, recent files
- ShortcutsDialog: grouped tree of all keyboard shortcuts (Help menu)
- Workspace tab context menu: Rename, Close, Close Others, Close to Right
- SQL query tab context menu: Rename, Duplicate, Close, Close Others
- Smart status bar: right-side connection indicator + selected cell preview
- File → Open SQL File (Ctrl+O), Save (Ctrl+S), Save As, Open Recent
- Recent files persisted to ~/.dbclient/recent_files.json (last 10)
- TableViewer.cell_selected signal wired to status bar cell preview
- EditorTab._filepath tracks the file associated with each query tab

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 17:00:31 -04:00
nngoandClaude Sonnet 4.6 b003976d39 theme: upgrade palette from Catppuccin Mocha to Frappé
Replaces all dark-Mocha colours (#1e1e2e base, #313244 surface0, etc.)
with the noticeably brighter Frappé variants (#303446 base, #414559
surface0, etc.) across the QSS and every hardcoded colour in Python
source files (syntax highlighter, completer popup, log viewer, explain
view, table viewer, schema browser, icons, etc.).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 16:51:37 -04:00
nngoandClaude Sonnet 4.6 22f1dd5f45 feat: SQL auto-complete, Find/Replace bar, and connection tab coloring
- Add SqlCompleter (sql_completer.py): schema-aware popup with keyword,
  table, view, and lazy column completions; dot-notation and Ctrl+Space
- Add FindBar to EditorTab: inline find/replace with wrap-around,
  case/whole-word flags, match count, and Ctrl+F / Ctrl+H shortcuts
- Color workspace tab text by connection profile in MainWindow
- Move Toggle History shortcut Ctrl+H → Ctrl+Shift+H to free Ctrl+H
  for Find & Replace

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 16:42:18 -04:00
nngoandClaude Sonnet 4.6 f680bfe3a3 Add schema-aware SQL auto-complete to the SQL editor
New file app/ui/sql_completer.py:
- SqlCompleter (QObject) owns a _CompletionPopup (QListWidget) that
  floats above the editor as a ToolTip-style frameless window styled
  to match the Catppuccin Mocha theme.
- SQL keyword pool sourced directly from syntax_highlighter._KEYWORDS,
  _TYPES, _FUNCTIONS so the two features stay in sync.
- Schema data (tables, views, columns) loaded asynchronously via
  SchemaWorker; a generation counter discards stale results when the
  database changes rapidly.
- Column loading is lazy: fires only when the user types "table."
  (dot-qualified prefix), avoiding upfront cost for wide schemas.
- _extract_prefix() uses a regex to detect both bare words and
  "table.col" dot notation; guards against completing inside string
  literals by counting unescaped quotes to the left of the cursor.
- _accept_completion() replaces only the typed prefix, preserving any
  qualifier already in the document (e.g. "orders.cu" → "orders.customer_id").

Changes to app/ui/sql_editor.py:
- CodeEditor.set_completer() attaches the completer and stores it.
- keyPressEvent priority order:
    1. Popup visible → Esc hides, Enter/Tab accepts, Up/Down navigates.
    2. Ctrl+Space → force-show completions (1-char minimum).
    3. Tab (no popup) → existing 4-space insert behaviour preserved.
    4. Ctrl+/ → existing comment-toggle preserved.
    5. All other keys → super() then auto-trigger (2-char minimum).
- focusOutEvent hides the popup when the editor loses focus.
- EditorTab._build_ui() creates SqlCompleter, attaches it, and calls
  set_context() if a driver is already present.
- EditorTab.set_context() updates driver/database and reloads schema.
- _maybe_invalidate_schema() re-fetches schema after CREATE/DROP/ALTER
  so newly created tables appear in completions immediately.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 16:28:58 -04:00
nngoandClaude Sonnet 4.6 04476bae11 Fix medium/low severity issues: script splitter, EXPLAIN, keyring, imports
execute_script() semicolon splitter (all drivers)
- Add BaseDriver._split_statements() that tracks single/double-quoted
  strings and -- / /* */ comments so semicolons inside procedure bodies
  are not treated as statement boundaries.
- Replace the naive sql.split(';') in all four drivers with this helper.
- MSSQL execute_script() also pre-splits on GO (case-insensitive, own
  line) so scripts pasted from SSMS work correctly.

MSSQL EXPLAIN
- SET SHOWPLAN_TEXT ON/execute/SET SHOWPLAN_TEXT OFF must be separate
  execute() calls; pyodbc rejects multiple statements in one call.
  Hold self._lock for the entire sequence to keep session state atomic.

Keyring (connections.py)
- Log warnings (with traceback) on load/save failures instead of
  silently returning "".
- save_password() now calls delete_password() when password is empty,
  so clearing a saved password actually removes the old keyring entry
  rather than leaving a stale one behind.

SQLite get_tables() O(N) lock acquisitions
- Run all COUNT(*) queries inside the same `with self._cur() as c:`
  block, reducing N+1 lock acquisitions to 1.

Unused import: remove psycopg2.extras from postgres_driver.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 16:12:40 -04:00
nngoandClaude Sonnet 4.6 4ccc81955e Fix high-severity issues: implement SSL and warn on raw-SQL filter
SSL was wired through the UI and ConnectionProfile but never actually
applied in any driver or passed from the connection builder.

- main_window: pass ssl/ssl_ca/ssl_cert/ssl_key from profile into
  the driver config dict so drivers can act on them
- MySQL: add ssl={ca,cert,key} to pymysql connect kwargs when enabled
- PostgreSQL: set sslmode=verify-ca (with CA) or require, plus
  sslrootcert/sslcert/sslkey when provided
- MSSQL: switch Encrypt=yes + TrustServerCertificate=no when SSL is
  on; Encrypt=no + TrustServerCertificate=yes otherwise
- SQLite: no change needed (local file, no network layer)

WHERE filter: add tooltip explicitly labelling the input as raw SQL
so users understand arbitrary expressions are executed directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 16:07:02 -04:00
nngoandClaude Sonnet 4.6 724594d3f1 Fix three critical bugs in ConnectionProfile and MSSQL driver
- Declare `password` as a proper dataclass field (repr=False) so it is
  visible to type checkers and any code path that constructs a bare
  ConnectionProfile; drop the now-unused Optional import.
- Fix _conn_str() fallback loop: the return was inside the for-body,
  so only ODBC Driver 18 was ever tried. Now uses pyodbc.drivers() to
  pick the first installed driver from the preference list.
- Make _cur() a thread-safe @contextmanager that holds self._lock for
  the cursor's lifetime, matching MySQL/PostgreSQL/SQLite. Updated all
  16 call sites to `with self._cur() as c:`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 16:02:04 -04:00
nngo b01ad5ea40 Initial Codes 2026-05-21 15:46:41 -04:00