Structure
A function or procedure that uses PL/pgSQL must specify thePLpgSQL language within the or statement:
- An optional
DECLAREsection that contains variable declarations for all variables that are used within the block and are not defined as or parameters. - A or body, consisting of statements enclosed by
BEGINandEND. - An optional
EXCEPTIONsection for catching and handlingSQLSTATEerrors.
EXIT statements.
$$). 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.
Syntax
Declare a variable
DECLARE specifies all variable definitions that are used in a block.
variable_nameis an arbitrary variable name.data_typecan be a supported , , or the PL/pgSQLREFCURSORtype, when declaring cursor variables.CONSTANTspecifies that the variable cannot be reassigned, ensuring that its value remains constant within the block.expressionis an expression that provides an optional default value for the variable. Default values are evaluated every time a block is entered in a function or procedure.
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/pgSQLREFCURSOR data type. For example:
CURSOR FOR syntax and specify the query:
REFCURSOR data type.
For information about opening and using cursors, see Open and use cursors.
Assign a result to a variable
Use the PL/pgSQLINTO clause to assign a result of a or mutation (, , ) statement to a specified variable. The optional STRICT clause specifies that the statement must return exactly one row; otherwise, the function or procedure will error. This behavior can be enabled by default using the session setting.
expressionis an expression that defines the result to be assigned to the variable.targetis an arbitrary variable name. This can be a list of comma-separated variables, or a single .
t with INT column col:
The following inserts a specified value x into the table, and the INTO clause assigns the to i.
NOTICE message that contains the inserted value:
max to find the maximum col value in table t, and assigns the result to i.
Write conditional statements
UseIF syntax to execute statements conditionally. PL/pgSQL understands several forms of IF statements.
IF ... THEN executes statements only if a boolean condition is true.
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.
IF, ELSE, and ELSIF conditions are not required to execute statements. You can exclude any statements or add a placeholder NULL statement.
Write loops
Write a loop to repeatedly execute statements. On its own,LOOP executes statements infinitely.
WHILE executes statements infinitely if a boolean condition is true. The statements repeat until the condition is false.
FOR ... IN iterates a loop over a range of integer values. Specify a variable name to iterate over the minimum and maximum values of the integer range. The optional BY clause is used to specify an integer step value, and REVERSE causes the loop to subtract the step value across iterations.
FOR loop iterates from 1 to 10 in steps of 2:
Control execution flow
EXIT
Add an EXIT statement to end a loop. An EXIT statement can be combined with an optional WHEN boolean condition.
EXIT statement to target a block that has a matching label. An EXIT statement with a label can target either a loop or a block. An EXIT statement inside a block must have a label.
The following EXIT statement will end the label block before the statements are executed.
If more than one PL/pgSQL block has a matching label, the innermost block is chosen.
EXIT statement in the inner block is used to exit the stored procedure.
RETURN
Add a RETURN statement to a routine with an OUT parameter or VOID return type to exit the routine immediately.
CONTINUE
Add a CONTINUE statement to end the current iteration of a loop, skipping any statements below CONTINUE and beginning the next iteration of the loop.
A CONTINUE statement can be combined with an optional WHEN boolean condition. In the following example, if a WHEN condition is defined and met, then CONTINUE causes the loop to skip the second group 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.
FOR clause.
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.FETCH statement to retrieve query results for a specified number of rows:
FETCH statements have to be issued within the same transaction, or the cursor will not be found:
Report messages and handle exceptions
Use theRAISE statement to print messages for status or error reporting.
RAISE messages the client directly, and does not currently produce log output.-
levelis the message severity. Possible values areDEBUG,LOG,NOTICE,INFO,WARNING, andEXCEPTION. SpecifyEXCEPTIONto raise an error that aborts the current transaction. -
messageis a message string to display. -
expressionsis 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. -
optionis a type of additional information to include. Possible values areMESSAGE,DETAIL,HINT, orERRCODE. To specifyMESSAGE, use the following alternate syntax: -
expressionis an expression to display that corresponds to the specifiedoption. IfERRCODEis the specified option, this must be a validSQLSTATEerror code or name.
Write exception logic
Use anEXCEPTION 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:
WHEN conditions are not required to execute statements. You can exclude any statements or add a placeholder NULL statement.
Control transactions
Use aCOMMIT or ROLLBACK statement within a PL/pgSQL to finish the current transaction and automatically start a new one.
- Any updates made within the previous transaction are either committed or rolled back, while PL/pgSQL variables keep their values.
- Execution of the stored procedure resumes in a new transaction with the statements immediately following the
COMMITorROLLBACKstatement.
COMMIT and ROLLBACK statements within PL/pgSQL blocks have the following requirements:
- They must be used inside a PL/pgSQL .
-
They cannot be inside a block with an
EXCEPTIONclause. - The procedure must be called directly in a SQL statement. It cannot be called by another stored procedure.
- The procedure must be called from an ; i.e., the call cannot be enclosed by and SQL statements.
COMMIT or ROLLBACK automatically start another PL/pgSQL transaction. If a transaction is running when the procedure ends, it is implicitly committed without having to be followed by a COMMIT.
SET TRANSACTION statements to set the of a PL/pgSQL transaction. In PL/pgSQL, SET TRANSACTION statements must directly follow COMMIT or ROLLBACK, or another SET TRANSACTION statement; and must precede the other statements in the transaction.
SET TRANSACTION statement uses the default settings.
Call a procedure
Use aCALL statement to call a procedure from within a PL/pgSQL or .
OUT parameters. For example, given the procedure:
output_one within another procedure:
OUT parameters can only be called from a PL/pgSQL routine. For another example, see .
Examples
Create a user-defined function using PL/pgSQL
The following user-defined function returns thenth 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: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.
rides_left cursor name:
vehicle_location_histories:
Example details
The example works as follows: defines a stored procedure calleddelete_earliest_histories with an INT and a REFCURSOR parameter.
LANGUAGE specifies as the language for the stored procedure.
DECLARE specifies the that are used in the procedure body.
BEGIN and END in the procedure body.
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.
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 .
Finally, the statement reports these values for each deleted row.
OPEN statement 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.
Known limitations
- It is not possible to use a variable as a target more than once in the same
INTOclause. For example,SELECT 1, 2 INTO x, x;. - PLpgSQL variable declarations cannot inherit the type of a table row or column using
%TYPEor%ROWTYPEsyntax. - PL/pgSQL arguments cannot be referenced with ordinals (e.g.,
$1,$2). - The following statements are not supported:
FORcursor loops,FORquery loops, andFOREACHloops.RETURN NEXTandRETURN QUERY.PERFORM,EXECUTE,GET DIAGNOSTICS, andCASE.
- PL/pgSQL exception blocks cannot catch .
RAISEstatements cannot be annotated with names of schema objects related to the error (i.e., usingCOLUMN,CONSTRAINT,DATATYPE,TABLE, orSCHEMA).RAISEstatements message the client directly, and do not produce log output.ASSERTdebugging checks are not supported.RECORDparameters and variables are not supported in .- Variable shadowing (e.g., declaring a variable with the same name in an inner block) is not supported in PL/pgSQL.
- Syntax for accessing members of composite types without parentheses is not supported.
NOT NULLvariable declarations are not supported.- Cursors opened in PL/pgSQL execute their queries on opening, affecting performance and resource usage.
- Cursors in PL/pgSQL cannot be declared with arguments.
OPEN FOR EXECUTEis not supported for opening cursors.- The
print_strict_paramsoption is not supported in PL/pgSQL. - The
FOUNDlocal variable, which checks whether a statement affected any rows, is not supported in PL/pgSQL. - By default, when a PL/pgSQL variable conflicts with a column name, CockroachDB resolves the ambiguity by treating it as a column reference rather than a variable reference. This behavior differs from PostgreSQL, where an ambiguous column error is reported, and it is possible to change the
plpgsql.variable_conflictsetting in order to prefer either columns or variables. - It is not possible to define a
RECORD-returning PL/pgSQL function that returns different-typed expressions from differentRETURNstatements. CockroachDB requires a consistent return type forRECORD-returning functions. - Variables cannot be declared with an associated collation using the
COLLATEkeyword. - Variables cannot be accessed using the
label.var_namepattern.

