Re: [PATCH] Add some documentation on how to call internal functions - Mailing list pgsql-hackers
From | Tom Lane |
---|---|
Subject | Re: [PATCH] Add some documentation on how to call internal functions |
Date | |
Msg-id | 933739.1729283023@sss.pgh.pa.us Whole thread Raw |
In response to | Re: [PATCH] Add some documentation on how to call internal functions (Pavel Stehule <pavel.stehule@gmail.com>) |
Responses |
Re: [PATCH] Add some documentation on how to call internal functions
|
List | pgsql-hackers |
Pavel Stehule <pavel.stehule@gmail.com> writes: > I'll mark this patch as ready for committer I spent a little time looking at this. I agree that it's better to show conversion of the example function's arguments to and from text* rather than leaving them as Datum. I also do think that we need to incorporate the example into src/tutorial, if only because that provides a chance to test it. And indeed, testing exposed that the example doesn't work: $ cd src/tutorial $ make $ psql postgres psql (18devel) Type "help" for help. postgres=# \i funcs.sql psql:funcs.sql:152: ERROR: could not determine which collation to use for string comparison HINT: Use the COLLATE clause to set the collation explicitly. The problem here is that we failed to pass through the result of PG_GET_COLLATION() to text_starts_with. We could do that, certainly, for a couple more lines of code. But it feels like this is getting into details that obscure the main point. I wonder if it'd be better to choose a different example that calls a non-collation-dependent target function. Anyway, proposed v3 attached. I folded the two patches into one, and editorialized on the text a little. regards, tom lane diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index af7864a1b5..03fa7633ef 100644 --- a/doc/src/sgml/xfunc.sgml +++ b/doc/src/sgml/xfunc.sgml @@ -2384,6 +2384,48 @@ PG_FUNCTION_INFO_V1(funcname); takes as its argument the actual value to return. </para> + <para> + To call another version-1 function, you can use + <function>DirectFunctionCall<replaceable>n</replaceable>(func, + arg1, ..., argn)</function>. This is particularly useful when you want + to call functions defined in the standard internal library, by using an + interface similar to their SQL signature. + </para> + + <para> + These convenience functions and similar ones can be found + in <filename>fmgr.h</filename>. + The <function>DirectFunctionCall<replaceable>n</replaceable></function> + family expect a C function name as their first argument. There are also + <function>OidFunctionCall<replaceable>n</replaceable></function> which + take the OID of the target function, and some other variants. All of + these expect the function's arguments to be supplied + as <type>Datum</type>s, and likewise they return <type>Datum</type>. + Note that neither arguments nor result are allowed to be NULL when + using these convenience functions. + </para> + + <para> + For example, to call the <function>starts_with(text, text)</function> + function from C, you can search through the catalog and find out that + its C implementation is the + <function>Datum text_starts_with(PG_FUNCTION_ARGS)</function> function. + So you would use <literal>DirectFunctionCall2(text_starts_with, + ...)</literal> to call it. + </para> + + <para> + <filename>fmgr.h</filename> also supplies macros that facilitate + conversions between C types and <type>Datum</type>. For example to + turn <type>Datum</type> into <type>text*</type>, you can + use <function>DatumGetTextPP(X)</function>. While some types have macros + named like <function>TypeGetDatum(X)</function> for the reverse + conversion, <type>text*</type> does not; it's sufficient to use the + generic macro <function>PointerGetDatum(X)</function> for that. + If your extension defines additional types, it is usually convenient to + define similar macros for your types too. + </para> + <para> Here are some examples using the version-1 calling convention: </para> @@ -2482,6 +2524,23 @@ concat_text(PG_FUNCTION_ARGS) memcpy(VARDATA(new_text) + arg1_size, VARDATA_ANY(arg2), arg2_size); PG_RETURN_TEXT_P(new_text); } + +/* A wrapper around starts_with(text, text) */ + +PG_FUNCTION_INFO_V1(t_starts_with); + +Datum +t_starts_with(PG_FUNCTION_ARGS) +{ + text *t1 = PG_GETARG_TEXT_PP(0); + text *t2 = PG_GETARG_TEXT_PP(1); + bool result; + + result = DatumGetBool(DirectFunctionCall2(text_starts_with, + PointerGetDatum(t1), + PointerGetDatum(t2))); + PG_RETURN_BOOL(result); +} ]]> </programlisting> @@ -2513,6 +2572,10 @@ CREATE FUNCTION copytext(text) RETURNS text CREATE FUNCTION concat_text(text, text) RETURNS text AS '<replaceable>DIRECTORY</replaceable>/funcs', 'concat_text' LANGUAGE C STRICT; + +CREATE FUNCTION t_starts_with(text, text) RETURNS boolean + AS '<replaceable>DIRECTORY</replaceable>/funcs', 't_starts_with' + LANGUAGE C STRICT; </programlisting> <para> diff --git a/src/tutorial/funcs.c b/src/tutorial/funcs.c index f597777a1f..73ecc023b1 100644 --- a/src/tutorial/funcs.c +++ b/src/tutorial/funcs.c @@ -11,6 +11,7 @@ #include "postgres.h" /* general Postgres declarations */ #include "executor/executor.h" /* for GetAttributeByName() */ +#include "utils/fmgrprotos.h" /* for text_starts_with() */ #include "utils/geo_decls.h" /* for point type */ PG_MODULE_MAGIC; @@ -102,6 +103,23 @@ concat_text(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(new_text); } +/* A wrapper around starts_with(text, text) */ + +PG_FUNCTION_INFO_V1(t_starts_with); + +Datum +t_starts_with(PG_FUNCTION_ARGS) +{ + text *t1 = PG_GETARG_TEXT_PP(0); + text *t2 = PG_GETARG_TEXT_PP(1); + bool result; + + result = DatumGetBool(DirectFunctionCall2(text_starts_with, + PointerGetDatum(t1), + PointerGetDatum(t2))); + PG_RETURN_BOOL(result); +} + /* Composite types */ PG_FUNCTION_INFO_V1(c_overpaid); diff --git a/src/tutorial/funcs.source b/src/tutorial/funcs.source index 542b5c81ec..eb56153754 100644 --- a/src/tutorial/funcs.source +++ b/src/tutorial/funcs.source @@ -123,16 +123,25 @@ SELECT * FROM EMP; ----------------------------- CREATE FUNCTION add_one(integer) RETURNS integer - AS '_OBJWD_/funcs' LANGUAGE C; + AS '_OBJWD_/funcs' LANGUAGE C STRICT; + +CREATE FUNCTION add_one(double precision) RETURNS double precision + AS '_OBJWD_/funcs' LANGUAGE C STRICT; CREATE FUNCTION makepoint(point, point) RETURNS point - AS '_OBJWD_/funcs' LANGUAGE C; + AS '_OBJWD_/funcs' LANGUAGE C STRICT; CREATE FUNCTION copytext(text) RETURNS text - AS '_OBJWD_/funcs' LANGUAGE C; + AS '_OBJWD_/funcs' LANGUAGE C STRICT; + +CREATE FUNCTION concat_text(text, text) RETURNS text + AS '_OBJWD_/funcs' LANGUAGE C STRICT; + +CREATE FUNCTION t_starts_with(text, text) RETURNS boolean + AS '_OBJWD_/funcs' LANGUAGE C STRICT; CREATE FUNCTION c_overpaid(EMP, integer) RETURNS boolean - AS '_OBJWD_/funcs' LANGUAGE C; + AS '_OBJWD_/funcs' LANGUAGE C STRICT; SELECT add_one(3) AS four; @@ -140,6 +149,8 @@ SELECT makepoint('(1,2)'::point, '(3,4)'::point ) AS newpoint; SELECT copytext('hello world!'); +SELECT t_starts_with('foobar', 'foo'); + SELECT name, c_overpaid(EMP, 1500) AS overpaid FROM EMP WHERE name = 'Bill' or name = 'Sam'; @@ -147,10 +158,13 @@ WHERE name = 'Bill' or name = 'Sam'; -- remove functions that were created in this file DROP FUNCTION c_overpaid(EMP, integer); +DROP FUNCTION t_starts_with(text, text); +DROP FUNCTION concat_text(text, text); DROP FUNCTION copytext(text); DROP FUNCTION makepoint(point, point); +DROP FUNCTION add_one(double precision); DROP FUNCTION add_one(integer); ---DROP FUNCTION clean_EMP(); +DROP FUNCTION clean_EMP(); DROP FUNCTION high_pay(); DROP FUNCTION new_emp(); DROP FUNCTION add_em(integer, integer);
pgsql-hackers by date: