Skip to main content
New in v23.2: PL/pgSQL is a procedural language that you can use within and in CockroachDB. In contrast to , which are issued one-by-one from the client to the database, PL/pgSQL statements are encapsulated in a block structure and executed on the database side, thus reducing network latency. PL/pgSQL enables more complex functionality than standard SQL, including conditional statements, loops, and exception handling. This page describes PL/pgSQL structure and syntax, and includes examples of functions and procedures that use PL/pgSQL.

Structure

A function or procedure that uses PL/pgSQL must specify the PLpgSQL language within the or statement:
PL/pgSQL is block-structured. A block contains the following: At the highest level, a PL/pgSQL block looks like the following:
When you create a function or procedure, you can enclose the entire PL/pgSQL block in dollar quotes ($$). Dollar quotes are not required, but are easier to use than single quotes, which require that you escape other single quotes that are within the function or procedure body.
For complete examples, see Create a user-defined function using PL/pgSQL and Create a stored procedure using PL/pgSQL.

Syntax

Declare a variable

DECLARE specifies all variable definitions that are used in the function or procedure body.
  • variable_name is an arbitrary variable name.
  • data_type can be a supported , , or the PL/pgSQL REFCURSOR type, when declaring cursor variables.
  • CONSTANT specifies that the variable cannot be reassigned, ensuring that its value remains constant within the block.
  • expression is an expression that provides an optional default value for the variable.
For example:

Declare cursor variables

A cursor encapsulates a selection query and is used to fetch the query results for a subset of rows. You can declare forward-only cursors as variables to be used within PL/pgSQL blocks. These must have the PL/pgSQL REFCURSOR data type. For example:
You can bind a cursor to a selection query within the declaration. Use the CURSOR FOR syntax and specify the query:
Note that the preceding cursor still has the REFCURSOR data type. For information about opening and using cursors, see Open and use cursors.

Assign a result to a variable

Use the PL/pgSQL INTO clause to assign a result of a or mutation (, , ) statement to a specified variable:
  • expression is an expression that defines the result to be assigned to the variable.
  • target is an arbitrary variable name. This can be a list of comma-separated variables, or a single .
For example, given a table t with INT column col: The following inserts a specified value x into the table, and the INTO clause assigns the to i.
When the procedure is called, it inserts the specified integer into a new row in the table, and prints a NOTICE message that contains the inserted value:
The following uses the max() to find the maximum col value in table t, and assigns the result to i.
When the function is invoked, it displays the maximum value that was inserted into the table:
For a more extensive example of variable assignment, see Create a stored procedure using PL/pgSQL.

Write conditional statements

Use IF syntax to execute statements conditionally. PL/pgSQL understands several forms of IF statements. IF... THEN executes statements only if a boolean condition is true.
For an example, see . IF... THEN... ELSE executes statements if a boolean condition is true. If the condition is false, the ELSE statements are executed.
IF... THEN... ELSIF executes statements if a boolean condition is true. If the condition is false, each ELSIF condition is evaluated until one is true. The corresponding ELSIF statements are executed. If no ELSIF conditions are true, no statements are executed unless an ELSE clause is included, in which case the ELSE statements are executed.
For usage examples of conditional statements, see Examples.

Write loops

Use looping syntax to repeatedly execute statements. On its own, LOOP executes statements infinitely.
On its own, WHILE executes statements infinitely if a boolean condition is true. The statements repeat until the condition is false.
For an example, see . Add an EXIT statement to end a LOOP or WHILE statement block. This should be combined with a conditional statement.
Add a CONTINUE statement to end a LOOP or WHILE statement block, skipping any statements below CONTINUE, and begin the next iteration of the loop. This should be combined with a conditional statement. In the following example, if the IF condition is met, then CONTINUE causes the loop to skip the second block of statements and begin again.

Open and use cursors

PL/pgSQL cursors can be used in the following scenarios:
  • When declared as variables, cursors can be used within PL/pgSQL blocks.
  • When specified as a parameter in a statement, cursors can be accessed externally from the stored procedure.
The cursor must first be opened within a PL/pgSQL block. If the cursor was declared without being bound to a query, you must specify a query using the FOR clause.
After opening the cursor, you can issue a PL/pgSQL FETCH statement to assign the result to one or more variables.
In PL/pgSQL, FETCH returns a single row. For example, FETCH 10 returns the 10th row.
You can free up a cursor variable by closing the cursor:
Cursors that are specified as parameters, rather than declared as variables, can be passed externally to and from PL/pgSQL blocks. For example, using the loaded by :
Within the same transaction that opened the cursor, use the SQL FETCH statement to retrieve query results for a specified number of rows:
The and FETCH statements have to be issued within the same transaction, or the cursor will not be found:

Report messages and handle exceptions

Use the RAISE statement to print messages for status or error reporting.
RAISE messages the client directly, and does not currently produce log output.
  • level is the message severity. Possible values are DEBUG, LOG, NOTICE, INFO, WARNING, and EXCEPTION. Specify EXCEPTION to raise an error that aborts the current transaction.
  • message is a message string to display.
  • expressions is an optional, comma-separated list of expressions that provide values to replace any % placed within the message string. The number of expressions must match the number of % placeholders.
  • option is a type of additional information to include. Possible values are MESSAGE, DETAIL, HINT, or ERRCODE. To specify MESSAGE, use the following alternate syntax:
  • expression is an expression to display that corresponds to the specified option. If ERRCODE is the specified option, this must be a valid SQLSTATE error code or name.
For example:

Write exception logic

Use an EXCEPTION statement to catch and handle specified errors. Any valid SQLSTATE error code or name can be specified, except for Class 40 (transaction rollback) errors. Arbitrary user-defined SQLSTATE codes can also be specified. If a specified error is caught, the exception handling statements are executed. Any unspecified errors are caught by WHEN OTHERS, except for query_canceled and assert_failure.
EXCEPTION logic is included after the main body of a PL/pgSQL block. For example:

Examples

Create a user-defined function using PL/pgSQL

The following returns the nth integer in the Fibonacci sequence. It uses the PL/pgSQL syntax to iterate through a simple calculation, and to return an error message if the specified n is negative.

Create a stored procedure using PL/pgSQL

Setup

To follow along, run to start a temporary, in-memory cluster with the sample dataset preloaded:
The following removes a specified number of earliest rides in vehicle_location_histories. It uses the PL/pgSQL 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 :
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. For more details on this example, see the .

Known limitations

  • PL/pgSQL blocks cannot be nested.
  • Cursors used in PL/pgSQL execute their queries on opening. This can affect performance and resource usage.
  • Cursors cannot be declared with parameters.
  • RECORD and ROW -type variables cannot be declared in PL/pgSQL.
  • NOT NULL variables cannot be declared in PL/pgSQL.
  • PL/pgSQL arguments cannot be referenced with ordinals (e.g., $1, $2 ).
  • PL/pgSQL EXCEPTION blocks cannot catch .
  • FOR loops (including FOR cursor loops and FOR query loops) and FOREACH loops are not supported.
  • RETURN NEXT and RETURN QUERY statements are not supported.
  • CASE statements are not supported.
  • EXIT and CONTINUE labels and conditions are not supported.
  • Variable shadowing (e.g., declaring a variable with the same name in an inner block) is not supported in PL/pgSQL.
  • When using the RAISE statement, schema objects related to the error cannot be named using COLUMN, CONSTRAINT, DATATYPE, TABLE, and SCHEMA.
  • The INTO statement in PL/pgSQL does not support the STRICT option.
  • PERFORM, EXECUTE, GET DIAGNOSTICS, and NULL statements are not supported for PL/pgSQL.

See also