Brings the snake_case rename (#7) and the app home and Runner (#9).
The developer panel's "open as a new cell" links now target the Runner
app (SqlWorkbenchRunnerAppMetaDataProducer.APP_NAME, prefixed), since
/app/sqlWorkbench is the home dashboard. Tests and README updated; the
changelog entry moves under 1.0.0-RC.3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Recent, failed and write runs sort by start time (not insert order).
- Activity by User has four columns (last run moves under the user), the
host readability rule's maximum.
- No widget-permission throw inside the renderer: the middleware checks
widget permissions, and host guards render every widget.
- Notebook record view: plain section titles (Cells, Shared With, Pinned
Results, Versions, Runs) instead of join names; Runs newest first,
Versions newest first; the Open in Runner widget is not a nested card.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `.editorconfig` (Kingsrook 3-space layout)
- `.gitattributes` (LF, CRLF for `*.cmd`)
- `.github/dependabot.yml` (Maven + Actions, grouped minor/patch, at most 3 open PRs, target `develop`)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner spec: the SQL Workbench app home is an information dashboard like
the other app homes, the notebook UI is its own nav entry ("Runner"), and
the nav lists only the main tables; sub-tables are relations on a notebook.
- Home: summary tiles and links, runs per day, recent notebooks, recent
runs, slowest and failed queries, most-used notebooks, shared notebooks,
committed writes, and (admins only) activity by user. Read through core
actions in the viewer's session, so permissions and record locks apply;
honest empty and no-permission states. Full width, inline styles only.
- Runner: sqlWorkbenchRunner child app showing the SQL_WORKBENCH widget
(/app/sqlWorkbenchRunner?notebook=<id>).
- Nav: Runner, sql_workbench_notebook, sql_workbench_execution. Cells,
versions, pinned results and shares stay standard tables (URL, API) as
child lists on the notebook; new Runs child list (notebook -> execution
join, locked to the viewer's runs) and an Open in Runner widget.
- SqlWorkbenchIntegrations: opt-in ESB and Quick Search hooks for the main
tables, no dependency on either module.
- Tests: home widgets (H2 and Postgres), nav structure, child lists,
hooks. README, CHANGELOG, UPGRADE, CLAUDE/AGENTS.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner rule: table names are lowercase snake_case, prefixed with the
qbit's name. Metadata only; the physical tables were already
sql_workbench_*, so no DDL.
- Tables, notebook PVS: sql_workbench_notebook, _cell, _notebook_version,
_result_snapshot, _notebook_share, _execution (now equal to their
backend table names)
- Enum PVS: sql_workbench_share_audience_type
- Permissions: sql_workbench.{hasAccess,write,share},
sql_workbench_admin.hasAccess, <table>.read etc.
- Security key types: sql_workbench_user (all-access
sql_workbench_all_access), sql_workbench_audience
- Joins: sql_workbench_notebook_join_sql_workbench_*
- tableNamePrefix / namespace normalized to snake_case, one "_"
- @Deprecated LEGACY_* constants with the pre-RC.3 names
- UPGRADE.md: permissions, security keys, stored references (audit_table,
saved views/reports) with sample SQL; CHANGELOG 1.0.0-RC.3
- Test for the new names, legacy constants and prefix normalization
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RC.4 lets partial edits of child rows pass joined record locks
(qqq#1073), which the table security matrix tests need.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add SqlWorkbenchDeveloperPanelProvider (id sqlWorkbench, order 450, icon
terminal), registered by SqlWorkbenchDeveloperPanelsMetaDataProducer via
DeveloperPanelsMetaData. For tables on an allowed RDBMS backend, and users
with the workbench access permission, it shows read-only SELECTs for the
record and its table in the backend's own table/column names (dialect
quoted identifiers, escaped primary-key literal), backend details, and
links that open each query in the workbench (?sql=&backend=, not run).
Build against QQQ 4.1.0-RC.4 (the developer panel contract).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
QQQ's UseTablePermissionCustomPermissionChecker needs a QContext instance
when available permissions are listed and threw a NullPointerException
there (seen in Website-Backend's permission-listing tests). The record
actions now use SqlWorkbenchPermissionChecker, which requires one named
session permission (share-table insert, notebook insert / edit). Test:
listing available permissions with no QContext.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From review of the standard-tables change:
- Versioning: the next number passes both currentVersion and the newest
stored version, the currentVersion update is checked, and failures are
logged (QQQ turns post-customizer exceptions into record warnings).
- Notebook delete cascade: a failed child delete aborts the delete.
- Share visibility sync checks its update.
- An admin ownership transfer resets ownerName to the new owner id.
- Shares are audited at record level, like the other tables.
- Tests: mixed insert/update batches, re-targeting rows into one's own
notebook, planting ownership, a shared reader vs pinned results, and
per-notebook children after denied deletes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Notebooks, cells, versions, pinned results, shares and the execution
audit are now ordinary QQQ tables in the SQL Workbench app (nav children,
record views with child lists, edit, export, audits, developer mode)
instead of hidden admin-only tables.
- Record security locks (core-enforced, so every entry point): owner OR
share audience for notebooks and their children (through joins), the
audit by user; admins hold sqlWorkbenchAllAccess. Key types
sqlWorkbenchUser / sqlWorkbenchAudience, applied to sessions by
SqlWorkbenchSecurity.applyToSession (no keys without workbench access).
- New sqlWorkbenchNotebookShare (USER / GROUP / ALL); publishing writes an
ALL share and visibility follows the shares.
- Per-table read/insert/edit/delete permissions, listed per role by the
config (user / share / admin).
- Table customizers hold the notebook invariants for the notebook UI and
the tables alike: owner from the session, cell validation and order,
one version per change, share keys, append-only versions and audit
(admins included), pinned results only from pinning, cascades.
- NotebookService and history run as the caller through the same actions;
explicit owner/share checks stay as defense in depth.
- Record actions: Share Notebook, Duplicate Notebook, Restore Version.
- DDL: share table plus an idempotent backfill of library shares.
- Tests: TableSecurityMatrixTest (role x table x action over the v1 table
API, core actions, export, PVS, record actions, notebook endpoints) and
a PostgreSQL lock test. Docs: README, DESIGN, CHANGELOG.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Re-pin the default-active qqq-snapshot profile from qqq-bom-pom
4.1.0-RC.1 to 4.1.0-RC.2 (now on Maven Central) and update the docs.
No SNAPSHOT dependencies.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without a least-privilege read-only role, read mode runs as the app
role and relies on the classifier (H2 / generic) or a READ ONLY
transaction that still allows side-effecting functions (Postgres).
Also records the security fixes in the CHANGELOG.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- SqlWorkbenchCredentials.getJdbcUrl is @JsonIgnore (a URL can carry a
password); it is only read in code when deriving the connection.
- CHANGELOG names the artifact com.kingsrook.qbits:qbit-sql-workbench.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Text cells starting with =, +, -, @, tab or CR are prefixed with a
quote so spreadsheets open them as text. Numbers are left alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A failure to open the target connection (driver messages can expose
hosts, users, URLs) now answers "Could not connect to backend X; see
the server log." with no sqlState, and logs the cause server-side.
Schema introspection does the same.
- An unexpected exception inside a started run finishes its audit row
as FAILED (generic message, detail logged) before it propagates.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MySQL runs the contents of /*! ... */ (and MariaDB /*M! ... */) and reads
/*+ ... */ hints, but the scanner skipped them as comments, so
"SELECT * FROM t /*! INTO OUTFILE '/tmp/x' */" classified as READ.
On the MySQL dialect the scanner now emits EXECUTABLE_COMMENT tokens for
these, and the classifier marks any statement containing one FORBIDDEN.
MySQL "--" also only starts a comment when whitespace follows ("1--1"
is arithmetic), so text after it is no longer hidden from the classifier.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Re-sending an in-flight executionUuid overwrote its registration, failed
the audit insert (UNIQUE), and the finally-unregister then removed the
original - escaping maxConcurrentExecutionsPerUser and cancellation.
- RunningStatements.register refuses an id already registered
(putIfAbsent); unregister(uuid, registration) removes only its own.
- The engine refuses a client id that is running or already in the
audit, before registering or recording anything
(DuplicateExecutionException; the route provider answers 409).
- ExecutionAuditor.record() checks the insert result and fails loudly.
- Execution ids are lower-cased at the API edge (execute, rows, export,
cancel, snapshots) and in engine.cancel.
- Tests for each, including 409 over HTTP.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The five tables used the same HAS_ACCESS_PERMISSION base (sqlWorkbench)
that every workbench user holds, so any user could read and write other
users' personal notebooks, cells, versions, pinned result snapshots and
the full execution audit through the generic /qqq/v1/table API.
- Tables now use adminPermissionBaseName (default sqlWorkbenchAdmin,
validated to differ from the access base name).
- Cell, version and snapshot tables drop insert/update/delete
capabilities, like the audit table.
- NotebookService and the history / snapshot audit lookups check the
caller in code, then read and write as the system user (SystemUser,
shared with ExecutionAuditor).
- Tests: a second ordinary user (and the owner) get permission denied
from the table query executor on all five tables; the workbench API
keeps working for ordinary users; admins can read everything.
- README / DESIGN document the admin permission.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
POST /execute takes an optional executionUuid (validated as a UUID) so
the UI can cancel a running statement before the response arrives.
Generated server-side when absent; the audit's unique key still
guarantees no reuse.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Design for a super-user SQL notebook qbit: read-only by default with a
gated, audited write mode over any QQQ RDBMS backend; notebooks with
versions, parameters and snapshots; REST API under
/qqq/v1/sql-workbench; Next UI SQL_WORKBENCH widget. Adds license,
notice, policy files and CODEOWNERS like the sibling qbits.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>