Pavel Stehule <[email protected]> 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);