Synopsis

Use the CREATE FUNCTION statement to create a function in a database.

Syntax

create_function ::= CREATE [ OR REPLACE ] FUNCTION subprogram_name ( 
                    [ arg_decl_with_dflt [ , ... ] ] )  
                    { RETURNS data_type
                      | RETURNS TABLE ( { column_name data_type } 
                        [ , ... ] ) }  
                    { unalterable_fn_attribute
                      | alterable_fn_only_attribute
                      | alterable_fn_and_proc_attribute } [ ... ]

arg_decl_with_dflt ::= arg_decl [ { DEFAULT | = } expression ]

arg_decl ::= [ arg_name ] [ arg_mode ] arg_type

subprogram_signature ::= arg_decl [ , ... ]

unalterable_fn_attribute ::= WINDOW
                             | LANGUAGE lang_name
                             | AS implementation_definition

lang_name ::= SQL | PLPGSQL | C

implementation_definition ::= ' sql_stmt_list '
                              | ' plpgsql_block_stmt '
                              | ' obj_file ' [ , ' link_symbol ' ]

sql_stmt_list ::= sql_stmt ; [ sql_stmt ... ]

alterable_fn_and_proc_attribute ::= SET configuration_parameter 
                                    { TO value
                                      | = value
                                      | FROM CURRENT }
                                    | RESET configuration_parameter
                                    | RESET ALL
                                    | [ EXTERNAL ] SECURITY 
                                      { INVOKER | DEFINER }

alterable_fn_only_attribute ::= volatility
                                | on_null_input
                                | PARALLEL parallel_mode
                                | [ NOT ] LEAKPROOF
                                | COST int_literal
                                | ROWS int_literal

volatility ::= IMMUTABLE | STABLE | VOLATILE

on_null_input ::= CALLED ON NULL INPUT
                  | RETURNS NULL ON NULL INPUT
                  | STRICT

parallel_mode ::= UNSAFE | RESTRICTED | SAFE

create_function

CREATEORREPLACEFUNCTIONsubprogram_name(,arg_decl_with_dflt)RETURNSdata_typeRETURNSTABLE(,column_namedata_type)unalterable_fn_attributealterable_fn_only_attributealterable_fn_and_proc_attribute

arg_decl_with_dflt

arg_declDEFAULT=expression

arg_decl

arg_namearg_modearg_type

subprogram_signature

,arg_decl

unalterable_fn_attribute

WINDOWLANGUAGElang_nameASimplementation_definition

lang_name

SQLPLPGSQLC

implementation_definition

'sql_stmt_list''plpgsql_block_stmt''obj_file','link_symbol'

sql_stmt_list

sql_stmt;sql_stmt;

alterable_fn_and_proc_attribute

SETconfiguration_parameterTOvalue=valueFROMCURRENTRESETconfiguration_parameterRESETALLEXTERNALSECURITYINVOKERDEFINER

alterable_fn_only_attribute

volatilityon_null_inputPARALLELparallel_modeNOTLEAKPROOFCOSTint_literalROWSint_literal

volatility

IMMUTABLESTABLEVOLATILE

on_null_input

CALLEDONNULLINPUTRETURNSNULLONNULLINPUTSTRICT

parallel_mode

UNSAFERESTRICTEDSAFE

'Regard the 'RETURNS' clause as mandatory.

The general introduction to the topic of user-defined subprograms explains that, for historical reasons, you can create a function, when a creating a procedure is the proper choice, by omitting the RETURNS clause and by giving it OUT or INOUT arguments.

Yugabyte recommends that you don't exploit this freedom.

For this reason, the RETURNS clause is shown as mandatory in the create_function syntax rule (though the PostgreSQL documentation shows it as optional).

Yugabyte further recommends that you avoid declaring out or inout arguments for a function.

When the purpose is to return more than just a single scalar value, you should create a dedicated composite type for the purpose and use this to declare the function's return value.

'create function' and the 'subprogram_signature' rule.

When you write a CREATE FUNCTION statement, you will already have decided what formal arguments it will have—i.e. for each, what will be its name, mode, data type, and optionally its default value. When, later, you alter or drop a function, you must identify it. You do this, in ALTER FUNCTION and DROP FUNCTION, typically by specifying just its subprogram_call_signature. You are allowed to use the full subprogram_signature. But this is unconventional. Notice that the subprogram_signature does not include the optional specification of default values; and you cannot mention these when you alter or drop a function. The distinction between the subprogram_signature and the subprogram_call_signature is discussed carefully in the section Subprogram overloading.

Semantics

  • The meanings of the various function attributes are explained in the section Subprogram attributes.

  • A function, like other schema objects such as a table, inevitably has an owner. You cannot specify the owner explicitly when a function is created. Rather, it's defined implicitly as what the built-in current_user function returns when it's invoked in the session that creates the function. This user must have the usage privilege on the function's schema, its argument data types, and its return data type. You (optionally) specify the function's schema and (mandatorily) its name within its schema as the argument of the subprogram_name rule.

  • If a function with the given name, schema, and argument types already exists then CREATE FUNCTION will draw an error unless the CREATE OR REPLACE FUNCTION variant is used.

  • Functions with different subprogram_call_signatures can share the same subprogram_name. (The same holds for procedures.) See section Subprogram overloading.

  • CREATE OR REPLACE FUNCTION doesn't change permissions that have been granted on an existing function. To use this statement, the current user must own the function, or be a member of the role that owns it.

  • In contrast, if you drop and then recreate a function, the new function is not the same entity as the old one. So you will have to drop existing objects that depend upon the old function. (Dropping the function CASCADE achieves this.) Alternatively, ALTER FUNCTION can be used to change most of the attributes of an existing function.

  • The languages supported by default are SQL, PLPGSQL and C.

'Create function' grants 'execute' to 'public'.

Execute is granted automatically to public when you create a new function. This is very unlikely to be want you want—and so this behavior presents a disguised security risk. Yugabyte recommends that your standard practice be to revoke this privilege immediately after creating a function.

You cannot set the 'depends on extension' attribute with 'create function'.

A function's depends on extension attribute cannot be set using CREATE [OR REPLACE] FUNCTION. You must use ALTER FUNCTION to set it.

Scalar functions and table functions

Use the appropriate variant of the RETURN clause to create either a scalar function or a table function. Notice that scalar can denote not just an atomic value of data types like int, numeric, text, and so on (and domains based on those data types); it can also denote a single composite value like that of a user-defined type.

Scalar function example

Try this:

drop type if exists x cascade;
create type x as (i int, t text);

drop function if exists f(int, text) cascade;
create function f(i in int, t in text)
  returns x
  language plpgsql
as $body$
begin
  return (i*2, t||t)::x;
end;
$body$;

WITH c as (select f(42, 'dog') as v)
select
  (v).i, (v).t
FROM c;

This is the result:

 i  |   t
----+--------
 84 | dogdog

Table function example:

Try this

drop table if exists t cascade;
create table t(k serial primary key, v varchar(4));
insert into t(v) values ('dog'), ('cat'), ('frog');

drop function if exists f() cascade;
create function f()
  returns table(z text)
  language plpgsql
as $body$
begin
  z := 'Starting content of t '; return next;
  z := '----------------------'; return next;
  for z in (select v from t order by k) loop
    return next;
  end loop;

  begin
    insert into t(v) values ('mouse');
  exception
    when string_data_right_truncation then
      z := ''; return next;
      z := 'string_data_right_truncation caught'; return next;
  end;

  insert into t(v) values ('bird');

  z := ''; return next;
  z := 'Finishing content of t'; return next;
  z := '----------------------'; return next;
  for z in (select v from t order by k) loop
    return next;
  end loop;
end;
$body$;

\t on
select z from f();
\t off

This is the result:

 Starting content of t
 ----------------------
 dog
 cat
 frog

 string_data_right_truncation caught

 Finishing content of t
 ----------------------
 dog
 cat
 frog
 bird

This kind of table function provides a convenient way to produce an arbitrarily formatted report that can easily be spooled to a file. This is because the select output is easily accessible (in ysqlsh) on stdout—and it's correspondingly easily accessible in client-side programming languages that do SQL like say, Python. In contrast, the output from raise info is tricky to capture (and definitely very hard to interleave in proper sequence with select results) because it comes on stderr.

Examples

Define a function using the SQL language.

create function mul(integer, integer) returns integer
    as 'select $1 * $2;'
    language sql
    immutable
    returns null on null input;

select mul(2,3), mul(10, 12);
 mul | mul
-----+-----
   6 | 120
(1 row)

Define a function using the PL/pgSQL language.

create or replace function inc(i integer)
  returns integer
  language plpgsql
as $body$
begin
  return i + 1;
end;
$body$;

select inc(2), inc(5), inc(10);
NOTICE:  Incrementing 2
NOTICE:  Incrementing 5
NOTICE:  Incrementing 10
 inc | inc | inc
-----+-----+-----
   3 |   6 |  11
(1 row)

See also