Skip to content

feat(routines): stored routine management (run/create/edit/drop) with plugin support#416

Open
debba wants to merge 5 commits into
mainfrom
feat/routine-management
Open

feat(routines): stored routine management (run/create/edit/drop) with plugin support#416
debba wants to merge 5 commits into
mainfrom
feat/routine-management

Conversation

@debba

@debba debba commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

What

Adds full management for stored procedures and functions, gated by a new routine_management driver capability (enabled for MySQL and PostgreSQL, opt-in for plugin drivers via manifest).

Run with parameters

  • New Run… entry in the routine context menu opens a modal that loads the routine's parameters and collects values: per-parameter NULL checkbox and a "Raw" toggle (skips string quoting; defaults on for numeric types). OUT parameters are shown as informational.
  • The driver builds a reviewable invocation script:
    • MySQL: OUT/INOUT go through session variables — SET @p = …; / CALL proc(@p); / SELECT @p AS p; — so outputs come back as a result set.
    • PostgreSQL: functions run as SELECT * FROM fn(...) (scalar and set-returning covered); procedures via CALL with NULL placeholders for OUT.
  • Navigation-initiated auto-runs are now multi-statement aware (runAutoQuery in Editor.tsx): scripts get split and routed through execute_query_batch, which runs on a single pooled connection — required for the session-variable dance to survive.

Create / Edit / Drop

  • New procedure/function ("+" on the Routines section): opens a dialect-aware starter template in an editor tab (DELIMITER-wrapped for MySQL, CREATE OR REPLACE … $$ … $$ for PostgreSQL).
  • Edit: opens a re-runnable script — MySQL wraps SHOW CREATE in DROP IF EXISTS + DELIMITER; PostgreSQL reuses pg_get_functiondef (already CREATE OR REPLACE).
  • Drop with confirmation modal. PostgreSQL resolves the exact identity signature via pg_get_function_identity_arguments and refuses to guess between overloads instead of dropping the wrong one.

Plugin integration

Four new DatabaseDriver methods (build_routine_call_sql, routine_create_template, get_routine_edit_script, drop_routine) ship dialect-neutral defaults, so existing drivers are untouched. For plugin drivers they are optional JSON-RPC methods: on "method not found" (-32601) the host falls back to the same generic SQL, so a plugin only implements what its dialect needs. Documented in the plugin-driver skill and added to plugins/manifest.schema.json (which is additionalProperties: false, so the capability had to land there too).

Also fixed in passing: routine context-menu actions on schema-scoped items (PostgreSQL sidebar) now carry the item's schema instead of always using the connection's active schema.

Testing

  • End-to-end against a live MariaDB through the real driver code path: invocation script with IN+OUT params executed via execute_batch returns the CALL's result set and then the OUT value (SELECT @p_result"got 7"); edit script, create template and drop verified working.
  • cargo test: 782 passed — 16 new unit tests for the SQL builders (session-variable rendering, INOUT SET prelude, identifier/literal escaping, hostile parameter names, PG identity-signature drop).
  • pnpm test: 2716 passed — 12 new tests for the parameter-assembly helper (routineCall.ts).
  • tsc --noEmit and eslint clean. New i18n keys added to all 8 locales.

Relationship with #415

Independent branches off main, but they compose: with #415 merged, a CALL returning multiple result sets shows every set in its own tab. Until then, the Run flow works but only the first result set of a multi-result procedure is displayed (pre-existing #414 behaviour). Expect a trivial conflict on the mod declarations in drivers/mysql/mod.rs and on tests.rs when merging the second of the two.

Limitations

  • SQLite has no stored routines; capability stays off and nothing changes there.
  • PostgreSQL routines with multiple overloads: run/edit target the first match from information_schema (pre-existing behaviour of parameters/definition lookups); drop refuses explicitly.

Adds full management for stored procedures and functions, gated by a new
routine_management driver capability (enabled for MySQL and PostgreSQL,
opt-in for plugins via manifest):

- Run with parameters: a modal collects IN/INOUT values (NULL checkbox,
  raw toggle defaulting on numeric types) and the driver builds a
  reviewable invocation script. MySQL OUT/INOUT parameters go through
  session variables (SET / CALL / SELECT @var); PostgreSQL functions run
  as SELECT * FROM fn(...) so set-returning functions come back as a
  result set.
- Create from template: dialect-aware starter scripts (DELIMITER-wrapped
  for MySQL, CREATE OR REPLACE with dollar quoting for PostgreSQL).
- Edit definition: re-runnable script (MySQL wraps SHOW CREATE in
  DROP IF EXISTS + DELIMITER; PostgreSQL reuses pg_get_functiondef).
- Drop with confirmation. PostgreSQL resolves the exact identity
  signature via pg_get_function_identity_arguments and refuses to guess
  between overloads.

The four new DatabaseDriver methods ship dialect-neutral defaults, so
existing drivers keep working. For plugin drivers they are OPTIONAL
JSON-RPC methods: on "method not found" the host falls back to the same
generic SQL, so a plugin only overrides what its dialect needs. The
manifest JSON schema documents the new capability.

Navigation-initiated auto-runs are now multi-statement aware: scripts
are split and routed through the batch path (single pooled connection),
which session-variable invocations require.

New i18n keys land in all 8 locales; SQL builders are covered by Rust
unit tests and the parameter-assembly helper by Vitest.
@debba debba requested a review from NewtTheWolf July 2, 2026 13:29
debba added 4 commits July 2, 2026 15:34
The Routines section introduced its own visual language: the Functions /
Procedures group headers used accordion-weight styling (uppercase,
semibold, tracking-wider) that competed with the section header, parameter
rows had a fixed-width mode column with a tiny icon, the empty state was
untranslated, and the header action buttons missed the mr-2.5 the other
sections use.

- Extract SidebarRoutineGroupHeader (shared by ExplorerSidebar,
  SidebarSchemaItem, SidebarDatabaseItem) styled like the inner group
  headers of table/view items.
- Restyle parameter rows to match SidebarColumnItem (font-mono, name +
  mode + type layout) behind a "parameters" group header mirroring the
  views' "columns" one; the function return value row is labelled via a
  new routines.returnValue key instead of rendering blank.
- MySQL: information_schema.parameters exposes the function return value
  at ordinal position 0 (NULL name/mode) which duplicated the row the
  driver already adds explicitly — filter it out.
- i18n: sidebar.parameters, sidebar.noParameters, routines.returnValue
  in all 8 locales; hover accent aligned to the triggers' yellow-400.
Match the other sections: refresh comes first and the new-routine plus
button after it; the Functions / Procedures group counts get the same
mr-2.5 as the section-header action icons so their right edges line up.
The nested "parameters" count (px-2, 8px) and the parameter type column
(px-3, 12px) ended short of the Functions / Procedures group counts
(18px). Compensate with mr-2.5 and mr-1.5 so every number and type in
the Routines section shares the same right edge.
The counts lined up with the plus button's box, but the button has p-1,
so the + glyph ends 4px further in. Shift counts and type column by 4px
(mr-2.5 -> mr-3.5, mr-1.5 -> mr-2.5) so they share the glyph's edge.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant