Skip to main content
A stored procedure is a database object consisting of PL/pgSQL or SQL statements that can be issued with a single CALL statement. This allows complex logic to be executed repeatedly within the database, which can improve performance and mitigate security risks. Both stored procedures and user-defined functions are types of routines. However, they differ in the following ways:
  • Functions return a value, and procedures do not return a value.
  • Procedures must be invoked using a CALL statement. Functions can be invoked in nearly any context, such as SELECT, FROM, and WHERE clauses, DEFAULT expressions, and computed column expressions.
  • Functions have volatility settings, and procedures do not.

Structure

A stored procedure consists of a name, optional parameters, language, and procedure body.
For details, see CREATE PROCEDURE.

Statement statistics

SQL statements executed within stored procedures are tracked in the SQL statistics subsystem and will appear in the SQL Activity > Statements page and the Insights page in the DB Console. This allows you to monitor the performance and execution statistics of individual statements within your procedures. When the stored procedure is invoked as part of a transaction, the statements executed within the procedure will also appear in the Transaction details in the Statement Fingerprints table.
Statement diagnostics cannot be collected for statements executed inside stored procedures. You can request statement diagnostics for the top-level invocation of the procedure, and the resulting trace includes spans for each statement executed. However, there is no way to target statements executed inside the procedure with a statement diagnostics request. For details, refer to Known limitations.

Examples

Setup

To follow along, run cockroach demo to start a temporary, in-memory cluster with the movr sample dataset preloaded:
For more examples of stored procedure creation, see CREATE PROCEDURE.

Create a stored procedure using PL/pgSQL

The following stored procedure removes a specified number of earliest rides in vehicle_location_histories. It uses the PL/pgSQL WHILE syntax to iterate through the rows, RAISE to return notice and error messages, and REFCURSOR to define a cursor that fetches the next rows to be affected by the procedure.
Open a transaction:
Call the stored procedure, specifying 5 rows to delete and a rides_left cursor name:
Use the cursor to fetch the 3 earliest remaining rows in vehicle_location_histories:
If the procedure is called again, these rows will be the first 3 to be deleted.

Example details

The example works as follows: CREATE PROCEDURE defines a stored procedure called delete_earliest_histories with an INT and a REFCURSOR parameter.
LANGUAGE specifies PL/pgSQL as the language for the stored procedure.
DECLARE specifies the PL/pgSQL variable definitions that are used in the procedure body.
BEGIN and END group the PL/pgSQL statements in the procedure body.
The following IF... THEN statement raises an exception if vehicle_location_histories has fewer rows than the number specified with num_deletions. If the exception is raised within an open transaction, the transaction will abort.
The following WHILE loop deletes rows iteratively from vehicle_location_histories, stopping when the number of loops reaches the num_deletions value. The DELETE... RETURNING... INTO statement assigns column values from each deleted row into separate variables. For more information about assigning variables, see Assign a result to a variable. Finally, the RAISE NOTICE statement reports these values for each deleted row.
The OPEN statement opens a cursor for all remaining rows in vehicle_location_histories, sorted by timestamp. After calling the procedure in an open transaction, the cursor can be used to fetch rows from the table.

Alter a stored procedure

The following statement renames the delete_earliest_histories example procedure to delete_histories:

Known limitations

Stored procedures have the following limitations:
  • Pausable portals are not supported with CALL statements for stored procedures.
  • COMMIT and ROLLBACK statements are not supported within nested procedures.
  • Routines cannot be invoked with named arguments, e.g., SELECT foo(a => 1, b => 2); or SELECT foo(b:= 1, a:= 2);.
  • Routines cannot be created if they reference temporary tables.
  • Routines cannot be created with unnamed INOUT parameters. For example, CREATE PROCEDURE p(INOUT INT) AS $$ BEGIN NULL; END; $$ LANGUAGE PLpgSQL;.
  • Routines cannot be created if they return fewer columns than declared. For example, CREATE FUNCTION f(OUT sum INT, INOUT a INT, INOUT b INT) LANGUAGE SQL AS $$ SELECT (a + b, b); $$;.
  • Routines cannot be created with an OUT parameter of type RECORD.
  • DDL statements (e.g., CREATE TABLE, CREATE INDEX ) are not allowed within UDFs or stored procedures.
  • Polymorphic types cannot be cast to other types (e.g., TEXT ) within routine parameters.
  • Routine parameters and return types cannot be declared using the ANYENUM polymorphic type, which is able to match any ENUM type.
  • Statement diagnostics cannot be collected for statements executed inside UDFs or stored procedures. You can request statement diagnostics for the top-level invocation of the function or procedure, and the resulting trace includes spans for each statement executed. However, there is no way to target statements executed inside the function or procedure with a statement diagnostics request.
  • Statements within routines do not currently respect hint injections. The workaround is to modify the inline hints directly in the body by replacing the routine.
Also refer to the PL/pgSQL known limitations.

See also