QoreDB LogoQoreDB
Back to blog
ProduitTechnical journal

Stored Procedures in QoreDB: Listing, Inspecting, and Dropping Without Reinventing the Engines

Stored functions and procedures have existed for a long time in relational databases. Each engine defines them in its own way: PL/pgSQL for PostgreSQL, the MySQL variant for MySQL and MariaDB, Transact-SQL for SQL Server. They are server objects, written in an engine-specific…

Raphaël – Creator of QoreDBRaphaël – Creator of QoreDB
5 min readUpdated Jul 18, 2026
Stored Procedures in QoreDB: Listing, Inspecting, and Dropping Without Reinventing the Engines

Stored functions and procedures have existed for a long time in relational databases. Each engine defines them in its own way: PL/pgSQL for PostgreSQL, the MySQL variant for MySQL and MariaDB, Transact-SQL for SQL Server. They are server objects, written in an engine-specific dialect, with name-resolution, overloading, and security rules that differ from one database to another.

In QoreDB, the goal was never to abstract away these differences. We wanted one precise thing: to be able to browse a database's existing routines, see their source code, and drop one with the same safeguards as any destructive operation. Here's how it's implemented.

A Common Contract Through the DataEngine Trait

Everything goes through the DataEngine trait that each driver implements. For routines, it exposes four methods: supports_routines(), list_routines(), get_routine_definition() and drop_routine(). The corresponding Tauri command first queries supports_routines() and returns a clear error if the engine doesn't have this notion. This lets the frontend hide the entry in the tree rather than displaying an empty section.

The returned type is a minimal Routine: namespace, name, type (Function or Procedure), argument signature, and possibly language and return type. Nothing is invented: the fields an engine doesn't expose stay empty. For MySQL, for instance, the parameter signature isn't returned in a usable form by information_schema, so the arguments field is empty on the MySQL side. We prefer honesty over approximate inference.

Listing Without Inference: What the Engine Exposes

The list is built through a direct query on the engine's catalogs. For the PostgreSQL family (PostgreSQL, TimescaleDB, CockroachDB, Supabase, Neon), we query pg_proc through a shared pg_compat module. For MySQL and MariaDB, we read information_schema.ROUTINES. For SQL Server, we use sys.objects with the P, FN, IF, and TF types. Pagination and name filtering are pushed to the server side: LIMIT/OFFSET for MySQL, OFFSET/FETCH for SQL Server. No list is loaded entirely into memory on the client side.

A filter by Function or Procedure type is applied to the same query, also server-side. This is an important point: the number of routines in an enterprise database can be high, and doing the sorting on the client side would break the fluidity of the sidebar.

Inspecting the Definition As It Was Written

A right-click on a routine opens a context menu with two actions: view the source, and drop. The source view retrieves the full definition through get_routine_definition(). Each driver returns the code as a CREATE OR REPLACE that the engine itself knows how to produce: pg_get_functiondef() for PostgreSQL, SHOW CREATE PROCEDURE for MySQL, OBJECT_DEFINITION for SQL Server. We don't reformat, we don't re-indent, we don't try to guess a pivot dialect. The code displayed is the one the engine returns.

This stance has a direct consequence: if you want to edit a routine, you copy the CREATE OR REPLACE into a SQL editor tab, modify the body, and run it. There is no graphical form for entering the routine's parameters and body. The reason is simple: each engine has its own grammar for security triggers, deterministic modes, OUT or INOUT parameters, and different language dialects. A universal form would either cover only a lowest common denominator or turn into a per-engine editor. We prefer to send the user into the SQL editor, with the exact code they already know.

Dropping Through the Security Interceptor

Dropping a routine is not a simple direct call to the driver. It goes through QoreDB's Universal Query Interceptor. The drop_routine command builds a pseudo statement DROP FUNCTION nom or DROP PROCEDURE nom, then calls pre_execute with the session context: environment (dev, staging, production), read-only mode, acknowledgment flag. If the environment is Production, a typed confirmation of the name is required by the frontend's DangerConfirmDialog.

The deletion follows exactly the same path as a DELETE or a DROP TABLE: same rules, same logging. This avoids having a back door that would bypass the protections in place elsewhere. If a custom security rule blocks the DROP, the user sees an explicit message with the name of the rule that was triggered.

The Engines That Don't Have This Notion

SQLite has no stored procedures. Neither does MongoDB, in the SQL sense of the term. Redis even less so. For these drivers, supports_routines() returns false. The interface then shows no Routines node in the tree. We don't display an empty placeholder with an explanatory message: the absence of an entry is the message.

We don't try to simulate stored procedures with client-side scripts. This is consistent with the rest of the product: QoreDB doesn't emulate features that an engine lacks. A MongoDB user sees their aggregation shell, not an invented pseudo-language that would pretend Mongo had CREATE PROCEDURE.

The Flow in Real Use

In practice, on a PostgreSQL database, you open the tree, expand the schema, and click on Routines. The list appears paginated and searchable by name. A right-click on an entry offers View Source or Drop. The source opens a read-only tab with the full CREATE OR REPLACE FUNCTION. To modify it, you copy it into an editable tab, change the body, hit Ctrl+Enter, and the routine is updated. To delete it, the deletion dialog asks you to retype the exact name if the environment is marked Production.

On MySQL, the flow is identical on the interface side. What changes is what the driver returns: the source comes from SHOW CREATE PROCEDURE, and the argument signature is absent. On SQL Server, the definition is retrieved through OBJECT_DEFINITION and table-valued functions are listed alongside the procedures. The interface behavior is identical; the content is engine-specific.

A Deliberate Scope Choice

Routine management in QoreDB fits within a minimal contract: list them, inspect them, drop them. Creation and modification go through the SQL editor with the engine's native syntax. This scope reflects a conviction: the real value of a database client, for these server objects, is to make visible what already exists and to secure destructive operations, not to invent an editing layer that would hide the engine's language. A backend developer writing PL/pgSQL needs a decent SQL editor, not an input form.

Newsletter

Stay updated on new releases

Subscribe to get product releases, new drivers notifications, and technical tutorials.

🎁 Bonus: Get our free SQL Performance Cheat Sheet — 9 pages, PostgreSQL / MySQL / SQLite (PDF)!
Share
Stored Procedures in QoreDB: Listing, Inspecting, and Dropping Without Reinventing the Engines - Blog - QoreDB