The body of a SQL function following AS
- should be a list of queries separated by whitespace characters and
- bracketed within quotation marks. Note that quotation marks used in
- the queries must be escaped, by preceding them with two
- backslashes.
+ should be a list of queries separated by semicolons and
+ bracketed within single-quote marks. Note that quote marks used in
+ the queries must be escaped, by preceding them with a backslash.
follows:
-select (x = TP1( 17,100.0));
+select TP1( 17,100.0);
EMP, and retrieves multiple results:
-select function hobbies (EMP) returns set of HOBBIES
+create function hobbies (EMP) returns setof HOBBIES
as 'select HOBBIES.* from HOBBIES
where $1.name = HOBBIES.person'
language 'sql';
- Notice that we defined a target list for the function
- (with the name RESULT), but the target list of the
- query that invoked the function overrode the function's
- target list. Hence, the result is labelled answer
+ Notice that we defined a column name for the function's result
+ (with the name RESULT), but this column name is not visible
+ outside the function. Hence, the result is labelled answer
instead of one.
return composite types, we must first introduce the
function notation for projecting attributes. The simple way
to explain this is that we can usually use the
- notation attribute(class) and class.attribute interchangably:
+ notations attribute(class) and class.attribute interchangably:
--
The target list order must be exactly the same as
that in which the attributes appear in the CREATE
- TABLE statement (or when you execute a .* query).
+ TABLE statement that defined the composite type.
- You must typecast the expressions (using ::) very carefully
- or you will see the following error:
-
+ You must typecast the expressions (using ::) to match the
+ composite type's definition, or you will get errors like this:
-NOTICE::function declared to return type EMP does not retrieve (EMP.*)
+ERROR: function declared to return emp returns varchar instead of text at column 1
but a user could use CREATE FUNCTION
to create additional alias names for an internal function.
+
+ Internal functions are declared in CREATE FUNCTION
+ with language name internal or
+ newinternal, depending on whether they follow the
+ old (pre-7.1) or new (7.1 and later) function call conventions.
+ The details of the call conventions are the same as for
+ C and newC functions respectively;
+ see the next section for details.
+
Functions written in C can be compiled into dynamically loadable
- objects, and used to implement user-defined SQL functions. The
- first time the user defined function is called inside the backend,
- the dynamic loader loads the function's object code into memory,
- and links the function with the running
-
Postgres executable. The SQL syntax
- for CREATE FUNCTION
- links the SQL function
- to the C source function in one of two ways. If the SQL function
- has the same name as the C source function the first form of the
- statement is used. The string argument in the AS clause is the
- full pathname of the file that contains the dynamically loadable
- compiled object. If the name of the C function is different from the
- desired name of the SQL function, then the second form is used. In this
- form the AS clause takes two string arguments, the first is the
- full pathname of the dynamically loadable object file, and the
- second is the link symbol that the dynamic loader should search
- for. This link symbol is just the function name in the C source
- code.
+ objects (also called shared libraries), and used to implement user-defined
+ SQL functions. The first time a user-defined function in a particular
+ loadable object file is called in a backend session,
+ the dynamic loader loads that object file into memory so that the
+ function can be called. The CREATE FUNCTION
+ for a user-defined function must therefore specify two pieces of
+ information for the function: the name of the loadable
+ object file, and the C name (link symbol) of the specific function to call
+ within that object file. If the C name is not explicitly specified then
+ it is assumed to be the same as the SQL function name.
- After it is used for the first time, a dynamically loaded, user
+ After it is used for the first time, a dynamically loaded user
function is retained in memory, and future calls to the function
- only incur the small overhead of a symbol table lookup.
+ in the same session will only incur the small overhead of a symbol table
+ lookup.
- The string which specifies the object file (the string in the AS
+ The string which specifies the object file (the first string in the AS
clause) should be the full path of the object
code file for the function, bracketed by quotation marks. If a
- link symbol is used in the AS clause, the link symbol should also be
+ link symbol is given in the AS clause, the link symbol should also be
bracketed by single quotation marks, and should be exactly the
same as the name of the function in the C source code. On Unix systems
the command nm will print all of the link
symbols in a dynamically loadable object.
- (
Postgres will not compile a function
- automatically; it must be compiled before it is used in a CREATE
- FUNCTION command. See below for additional information.)
+
+
+
Postgres will not compile a function
+ automatically; it must be compiled before it is used in a CREATE
+ FUNCTION command. See below for additional information.
+
+
+
+
+ Two different calling conventions are currently used for C functions.
+ The "old style" (pre-
Postgres-7.1) method
+ is selected by writing language name 'C' in the
+ CREATE FUNCTION command, while the "new style"
+ (7.1 and later) method is selecting by writing language name
+ 'newC'. Old-style functions are now deprecated
+ because of portability problems and lack of functionality, but they
+ are still supported for compatibility reasons.
-
C Language Functions on Base Types
+
Base Types in C-Language Functions
The following table gives the C type required for parameters in the C
|
int2
- int2
+ int2 or int16
include/postgres.h
|
|
int4
- int4
+ int4 or int32
include/postgres.h
|
Only pointers to such types can be used when passing
them in and out of
Postgres functions.
+ To return a value of such a type, allocate the right amount of
+ memory with palloc(), fill in the allocated memory,
+ and return a pointer to it.
+
+
Finally, all variable-length types must also be passed
by reference. All variable-length types must begin
with a length field of exactly 4 bytes, and all data to
Now that we've gone over all of the possible structures
- for base types, we can show some examples of real functions.
- Suppose funcs.c look like:
+ for base types, we can show some examples of real functions.
+
+
+
+
+
Old-style Calling Conventions for C-Language Functions
+
+ We present the "old style" calling convention first --- although
+ this approach is now deprecated, it's easier to get a handle on
+ initially. In the "old style" method, the arguments and result
+ of the C function are just declared in normal C style, but being
+ careful to use the C representation of each SQL data type as shown
+ above.
+
+
+ Here are some examples:
#include <string.h>
int
add_one(int arg)
{
- return(arg + 1);
+ return arg + 1;
}
/* By Reference, Fixed Length */
+float8 *
+add_one_float8(float8 *arg)
+{
+ float8 *result = (float8 *) palloc(sizeof(float8));
+
+ *result = *arg + 1.0;
+
+ return result;
+}
+
Point *
-makepoint(Point *pointx, Point *pointy )
+makepoint(Point *pointx, Point *pointy)
{
Point *new_point = (Point *) palloc(sizeof(Point));
* VARSIZE is the total size of the struct in bytes.
*/
text *new_t = (text *) palloc(VARSIZE(t));
- memset(new_t, 0, VARSIZE(t));
- VARSIZE(new_t) = VARSIZE(t);
+ VARATT_SIZEP(new_t) = VARSIZE(t);
/*
* VARDATA is a pointer to the data region of the struct.
*/
memcpy((void *) VARDATA(new_t), /* destination */
(void *) VARDATA(t), /* source */
- VARSIZE(t)-VARHDRSZ); /* how many bytes */
- return(new_t);
+ VARSIZE(t)-VARHDRSZ); /* how many bytes */
+ return new_t;
}
text *
text *new_text = (text *) palloc(new_text_size);
memset((void *) new_text, 0, new_text_size);
- VARSIZE(new_text) = new_text_size;
+ VARATT_SIZEP(new_text) = new_text_size;
strncpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1)-VARHDRSZ);
strncat(VARDATA(new_text), VARDATA(arg2), VARSIZE(arg2)-VARHDRSZ);
- return (new_text);
+ return new_text;
}
- On
OSF/1 we would type:
+ Supposing that the above code has been prepared in file
+ funcs.c and compiled into a shared object,
+ we could define the functions to
Postgres
+ with commands like this:
CREATE FUNCTION add_one(int4) RETURNS int4
- AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c';
+ AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c'
+ WITH (isStrict);
-CREATE FUNCTION makepoint(point, point) RETURNS point
- AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c';
+-- note overloading of SQL function name add_one()
+CREATE FUNCTION add_one(float8) RETURNS float8
+ AS 'PGROOT/tutorial/funcs.so',
+ 'add_one_float8'
+ LANGUAGE 'c' WITH (isStrict);
-CREATE FUNCTION concat_text(text, text) RETURNS text
- AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c';
+CREATE FUNCTION makepoint(point, point) RETURNS point
+ AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c'
+ WITH (isStrict);
CREATE FUNCTION copytext(text) RETURNS text
- AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c';
+ AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c'
+ WITH (isStrict);
+
+CREATE FUNCTION concat_text(text, text) RETURNS text
+ AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c'
+ WITH (isStrict);
- On other systems, we might have to make the filename
- end in .sl (to indicate that it's a shared library).
+ Here PGROOT stands for the full path to
+ the
Postgres source tree. Note that
+ depending on your system, the filename for a shared object might
+ not end in .so, but in .sl
+ or something else; adapt accordingly.
+
+
+ Notice that we have specified the functions as "strict", meaning that
+ the system should automatically assume a NULL result if any input
+ value is NULL. By doing this, we avoid having to check for NULL inputs
+ in the function code. Without this, we'd have to check for NULLs
+ explicitly, for example by checking for a null pointer for each
+ pass-by-reference argument. (For pass-by-value arguments, we don't
+ even have a way to check!)
+
+
+ Although this old-style calling convention is simple to use,
+ it is not very portable; on some architectures there are problems
+ with passing smaller-than-int data types this way. Also, there is
+ no simple way to return a NULL result, nor to cope with NULL arguments
+ in any way other than making the function strict. The new-style
+ convention, presented next, overcomes these objections.
-
C Language Functions on Composite Types
+
New-style Calling Conventions for C-Language Functions
+
+ The new-style calling convention relies on macros to suppress most
+ of the complexity of passing arguments and results. The C declaration
+ of a new-style function is always
+ Datum funcname(PG_FUNCTION_ARGS)
+
+ Each actual argument is fetched using a PG_GETARG_xxx() macro that
+ corresponds to the argument's datatype, and the result is returned
+ using a PG_RETURN_xxx() macro for the return type.
+
+
+ Here we show the same functions as above, coded in new style:
+
+#include <string.h>
+#include "postgres.h"
+#include "fmgr.h"
+
+/* By Value */
+
+Datum
+add_one(PG_FUNCTION_ARGS)
+{
+ int32 arg = PG_GETARG_INT32(0);
+
+ PG_RETURN_INT32(arg + 1);
+}
+
+/* By Reference, Fixed Length */
+
+Datum
+add_one_float8(PG_FUNCTION_ARGS)
+{
+ /* The macros for FLOAT8 hide its pass-by-reference nature */
+ float8 arg = PG_GETARG_FLOAT8(0);
+
+ PG_RETURN_FLOAT8(arg + 1.0);
+}
+
+Datum
+makepoint(PG_FUNCTION_ARGS)
+{
+ Point *pointx = PG_GETARG_POINT_P(0);
+ Point *pointy = PG_GETARG_POINT_P(1);
+ Point *new_point = (Point *) palloc(sizeof(Point));
+
+ new_point->x = pointx->x;
+ new_point->y = pointy->y;
+
+ PG_RETURN_POINT_P(new_point);
+}
+
+/* By Reference, Variable Length */
+
+Datum
+copytext(PG_FUNCTION_ARGS)
+{
+ text *t = PG_GETARG_TEXT_P(0);
+ /*
+ * VARSIZE is the total size of the struct in bytes.
+ */
+ text *new_t = (text *) palloc(VARSIZE(t));
+ VARATT_SIZEP(new_t) = VARSIZE(t);
+ /*
+ * VARDATA is a pointer to the data region of the struct.
+ */
+ memcpy((void *) VARDATA(new_t), /* destination */
+ (void *) VARDATA(t), /* source */
+ VARSIZE(t)-VARHDRSZ); /* how many bytes */
+ PG_RETURN_TEXT_P(new_t);
+}
+
+Datum
+concat_text(PG_FUNCTION_ARGS)
+{
+ text *arg1 = PG_GETARG_TEXT_P(0);
+ text *arg2 = PG_GETARG_TEXT_P(1);
+ int32 new_text_size = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ;
+ text *new_text = (text *) palloc(new_text_size);
+
+ memset((void *) new_text, 0, new_text_size);
+ VARATT_SIZEP(new_text) = new_text_size;
+ strncpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1)-VARHDRSZ);
+ strncat(VARDATA(new_text), VARDATA(arg2), VARSIZE(arg2)-VARHDRSZ);
+ PG_RETURN_TEXT_P(new_text);
+}
+
+
+
+ The CREATE FUNCTION commands are the same as
+ for the old-style equivalents, except that the language is specified
+ as 'newC' not 'C'.
+
+
+ At first glance, the new-style coding conventions may appear to be
+ just pointless obscurantism. However, they do offer a number of
+ improvements, because the macros can hide unnecessary detail.
+ An example is that in coding add_one_float8, we no longer need to
+ be aware that float8 is a pass-by-reference type. Another example
+ is that the GETARG macros for variable-length types hide the need
+ to deal with fetching "toasted" (compressed or out-of-line) values.
+ The old-style copytext and concat_text functions shown above are
+ actually wrong in the presence of toasted values, because they don't
+ call pg_detoast_datum() on their inputs.
+
+
+ The new-style function call conventions also make it possible to
+ test for NULL inputs to a non-strict function, return a NULL result
+ (from either strict or non-strict functions), return "set" results,
+ and implement trigger functions and procedural-language call handlers.
+ For more details see src/backend/utils/fmgr/README.
+
+
+
+
+
Composite Types in C-Language Functions
Composite types do not have a fixed layout like C
bool
c_overpaid(TupleTableSlot *t, /* the current instance of EMP */
- int4 limit)
+ int32 limit)
{
- bool isnull = false;
- int4 salary;
- salary = (int4) GetAttributeByName(t, "salary", &isnull);
+ bool isnull;
+ int32 salary;
+
+ salary = DatumGetInt32(GetAttributeByName(t, "salary", &isnull));
if (isnull)
return (false);
- return(salary > limit);
+ return salary > limit;
+}
+
+/* In new-style coding, the above would look like this: */
+
+Datum
+c_overpaid(PG_FUNCTION_ARGS)
+{
+ TupleTableSlot *t = (TupleTableSlot *) PG_GETARG_POINTER(0);
+ int32 limit = PG_GETARG_INT32(1);
+ bool isnull;
+ int32 salary;
+
+ salary = DatumGetInt32(GetAttributeByName(t, "salary", &isnull));
+ if (isnull)
+ PG_RETURN_BOOL(false);
+ /* Alternatively, we might prefer to do PG_RETURN_NULL() for null salary */
+
+ PG_RETURN_BOOL(salary > limit);
}
GetAttributeByName is the
Postgres system function that
returns attributes out of the current instance. It has
- three arguments: the argument of type TUPLE passed into
+ three arguments: the argument of type TupleTableSlot* passed into
the function, the name of the desired attribute, and a
- return parameter that describes whether the attribute
- is null. GetAttributeByName will
- align data properly so you can cast its return value to
- the desired type. For example, if you have an attribute
- name which is of the type name, the GetAttributeByName
- call would look like:
-
-char *str;
-...
-str = (char *) GetAttributeByName(t, "name", &isnull)
-
+ return parameter that tells whether the attribute
+ is null. GetAttributeByName returns a Datum
+ value that you can convert to the proper datatype by using the
+ appropriate DatumGetXXX() macro.