Add documentation about calling version-1 C functions from C.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 17 Jan 2025 19:37:38 +0000 (14:37 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 17 Jan 2025 19:37:38 +0000 (14:37 -0500)
This topic wasn't really covered before, so fill in some details.

Author: Florents Tselai <florents.tselai@gmail.com>
Reviewed-by: Pavel Stehule <pavel.stehule@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/90853055-5BBD-493D-91E5-721677C7C59B@gmail.com

doc/src/sgml/xfunc.sgml
src/tutorial/funcs.c
src/tutorial/funcs.source

index af7864a1b5b2ae97426a67682fd7b7b4630fb7f7..9f22dacac7d3c39f69e1d26d4cf223d59fb175a6 100644 (file)
@@ -2384,6 +2384,56 @@ 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.  Typically you would
+     use <literal>DirectFunctionCall2(text_starts_with, ...)</literal> to
+     call such a function.  However, <function>starts_with(text,
+     text)</function> requires collation information, so it will fail
+     with <quote>could not determine which collation to use for string
+     comparison</quote> if called that way.  Instead you must
+     use <literal>DirectFunctionCall2Coll(text_starts_with, ...)</literal>
+     and provide the desired collation, which typically is just passed
+     through from <function>PG_GET_COLLATION()</function>, as shown in the
+     example below.
+    </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 +2532,25 @@ 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);
+    Oid         collid = PG_GET_COLLATION();
+    bool        result;
+
+    result = DatumGetBool(DirectFunctionCall2Coll(text_starts_with,
+                                                  collid,
+                                                  PointerGetDatum(t1),
+                                                  PointerGetDatum(t2)));
+    PG_RETURN_BOOL(result);
+}
 ]]>
 </programlisting>
 
@@ -2513,6 +2582,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>
index f597777a1ffade51b58785355b6f2fd08f40b0d5..4a61177567c2b80d1b4cc0cea2a4cf1791a59c45 100644 (file)
@@ -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,25 @@ 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);
+   Oid         collid = PG_GET_COLLATION();
+   bool        result;
+
+   result = DatumGetBool(DirectFunctionCall2Coll(text_starts_with,
+                                                 collid,
+                                                 PointerGetDatum(t1),
+                                                 PointerGetDatum(t2)));
+   PG_RETURN_BOOL(result);
+}
+
 /* Composite types */
 
 PG_FUNCTION_INFO_V1(c_overpaid);
index 542b5c81ec95155d37997ff90e957727e1f9b0ae..eb56153754220489b27fe8c2121b21d0a442752e 100644 (file)
@@ -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);