Repository navigation
The results grid sorts, filters and totals in the page, and a result downloads as Excel CSV, JSON, Markdown or SQL INSERT. The editor formats SQL. A person can withdraw an auto-approve window request, and a request shows its steps. An approver can approve RO or RW, never DDL. The release adds migrations 195 and 196. Patch bump.
Added
- Sort, filter, sum and JSON view in the results grid. Each column header has a sort
button. It cycles ascending, descending and the original order. A filter box above the
grid keeps the rows that contain the text. Writecolumn:valueto search one column.
Both work on results of up to 5,000 rows. A larger result shows the filter box off,
with the reason as its tooltip. A dragged column width stays while you sort. When you
select two or more cells, a bar shows the count, sum, average, minimum and maximum. A
JSON cell opens as indented text in the full value view, and a Raw button shows the
original. - The Export menu offers the new formats. It has four more items: CSV for Excel, JSON,
Markdown table and SQL INSERT. They call theresult.*routes that the entry below
describes. A refusal, such as a result that is too large, shows its message in a toast
and saves no file. The menu opens upward when the window has little room below it. - Format SQL in the editor. Shift+Alt+F lays out the selected text, or the whole text
when the selection is empty. It uses thesql-formatterpackage with the dialect of the
connection's engine. It changes spaces and line breaks only, and keeps case, quotes,
casts and comments. A Format button next to the word wrap button does the same. One
Undo restores the text and the selection. The raw prototype has no formatter, so the
key does nothing there and the button is absent. - More ways to download a result.
GET /api/queries/{id}/result.json,result.mdand
result.sqlreturn the stored, masked result as JSON, a Markdown table or INSERT
statements for a placeholder table name. A result with more rows than
result_export_max_rows(default 100000, migration 196) answers 413.result.csv?bom=1adds a UTF-8
byte order mark, so that Excel shows Turkish letters right. The stored file does not
change. Add?statement=Nto pick one table of a multi-statement result. - The audit screen says when one rule does most of the auto-approving.
GET /api/admin/audit/insightlooks at the queries that QueryHub approved by itself
in the last 30 days and groups them by rule and by the tier of the query: RO,
or RW for writes. For a tier with at least 20 such queries, where one rule
produced at least half of them, the answer names that rule, its count and its
share. Otherwise the list is empty. The audit screen shows the answer in a
callout with a "Review rule" button that opens the Auto-approve screen. The
route uses the same gate as the audit search. - Access shows when it ends.
GET /api/connectionsaddsgrantEndsAtto each
database entry: an ISO time, or null for no end. The value is the end of the
access the person holds at the highest tier there. When two grants give the same
tier, the one that ends last counts. A grant with no end makes it null. The
Welcome lists show "44d left" or "Ends soon · 47h left" on each row. The tree
shows an amber "Ends in 47h" chip when the end is within seven days. - A request shows its steps.
GET /api/queries/{id}addstimeline: the steps
sent, decided, started and finished, with the time of each. Only the steps that
happened appear. A decided step carries the outcome and the name of the person
who decided. The Messages tab shows them in a "Steps" list. The list endpoints
do not carry it. - A person can withdraw an auto-approve window request.
POST /api/auto-approve-requests/{id}/withdrawwithdraws a request that nobody has
decided. A request of another person answers 404not_found. A request that is
decided answers 409already_decided. Migration 195 adds the statuswithdrawn.
The request dialog has a Withdraw button. A DBA decision on a withdrawn request
answers 409 "The person withdrew this request." - A grant revoke can carry a reason.
DELETE /api/admin/grants/{gid}and
DELETE /api/admin/grants/fleet/{gid}accept an optional body{"reason": ...}
with at most 500 characters. The reason goes into the audit row and nowhere
else. A request with no body still works. Both revoke rows have an optional
"Reason" field. - Three outbox events for SQL requests.
queryhub.request_decided,
queryhub.request_withdrawnandqueryhub.request_expiredgo to the requester.
The settingidp_outbox_enabled(default off) gates them, as it gates the
other events.docs/AUTH.mdsection 1.3 lists the payload keys.
Changed
- Destructive admin actions ask inline. Removing a team, an owner, a role, a
mask exemption or a connection, disabling a connection, and pausing or resuming
an auto-approve rule now open a row with the same sentence and two buttons. The
browser dialog is gone. The kill switch history shows a name in "By" and in
"released by" when the people list knows the id. - An approver can approve RO or RW, never DDL. A DBA scope (
POSTandPUT /api/admin/scopes) with DDL as its highest tier answers HTTP 400 with the code
tier_not_allowed. So does a roleapprover(POSTandPATCH /api/admin/roles) with a DDL ceiling or with no ceiling, because no ceiling
means every tier. The scope form shows RO and RW. The role form offers RO and
RW and has no "No ceiling". A super-admin and a roleadminare not
affected. The database has no constraint, so a row that SQL writes still works.
Rows that exist keep working: none holds DDL on the live system today, and a
stored row with DDL opens in the form with Save off until you pick RO or RW.
Code:src/queryhub/approver_limits.py.docs/OPERATIONS.mdsection 32.