agora inbox for pgsql-hackers@postgresql.org
help / color / mirror / Atom feedFrom: Chapman Flack <chap@anastigmatix.net>
Subject: [PATCH 4/4] PL/Sample "with" TRANSFORM FOR TYPE
Date: Mon, 21 Feb 2022 21:02:59 -0500
Add just enough TRANSFORM handling to PL/Sample for it to announce
what functions would be called (in a real implementation) on which
return or argument types.
Add a test based on creating a completely bogus transform out of
existing functions that have the right parameter and return types
but otherwise no business at all being used for a transform. Because
PL/Sample will not really call them, that will work as a test.
In passing: turn a SearchSysCache(..., 0, 0, 0) to SearchSysCache1
so the example code is following the recommended practice.
---
src/test/modules/plsample/expected/plsample.out | 31 +++++++++
src/test/modules/plsample/plsample.c | 84 ++++++++++++++++++++++++-
src/test/modules/plsample/sql/plsample.sql | 18 ++++++
3 files changed, 131 insertions(+), 2 deletions(-)
diff --git a/src/test/modules/plsample/expected/plsample.out b/src/test/modules/plsample/expected/plsample.out
index a0c318b..0e7850e 100644
--- a/src/test/modules/plsample/expected/plsample.out
+++ b/src/test/modules/plsample/expected/plsample.out
@@ -34,3 +34,34 @@ NOTICE: argument: 0; name: a1; value: {foo,bar,hoge}
(1 row)
+/*
+ * Create a bogus transform using a couple existing functions that
+ * happen to have the right parameter and return types. Calamity does
+ * not ensue, because plsample will merely announce what transform functions
+ * it "would" apply, and not really call them.
+ */
+CREATE TRANSFORM FOR text LANGUAGE plsample (
+ FROM SQL WITH FUNCTION prsd_lextype, TO SQL WITH FUNCTION textrecv
+);
+CREATE OR REPLACE FUNCTION
+ plsample_result_text(a1 numeric, a2 text, a3 integer[])
+RETURNS text
+TRANSFORM FOR TYPE text
+AS $$
+ Example of source with text result.
+$$ LANGUAGE plsample;
+SELECT plsample_result_text(1.23, 'abc', '{4, 5, 6}');
+NOTICE: source text of function "plsample_result_text":
+ Example of source with text result.
+
+NOTICE: argument: 0; name: a1; value: 1.23
+NOTICE: argument: 1; name: a2; transformed with: prsd_lextype(internal)
+NOTICE: argument: 2; name: a3; value: {4,5,6}
+NOTICE: return value: transformed with: textrecv(internal)
+ plsample_result_text
+---------------------------------------
+ +
+ Example of source with text result.+
+
+(1 row)
+
diff --git a/src/test/modules/plsample/plsample.c b/src/test/modules/plsample/plsample.c
index 72693f6..7324523 100644
--- a/src/test/modules/plsample/plsample.c
+++ b/src/test/modules/plsample/plsample.c
@@ -22,6 +22,7 @@
#include "funcapi.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+#include "utils/regproc.h" /* only for format_procedure() */
#include "utils/syscache.h"
PG_MODULE_MAGIC;
@@ -92,18 +93,21 @@ plsample_func_handler(PG_FUNCTION_ARGS)
Form_pg_proc pl_struct;
volatile MemoryContext proc_cxt = NULL;
Oid *argtypes;
+ List *trftypes;
char **argnames;
char *argmodes;
char *proname;
Form_pg_type pg_type_entry;
Oid result_typioparam;
Oid prorettype;
+ Oid prolang;
+ Oid tosql;
FmgrInfo result_in_func;
int numargs;
/* Fetch the source text of the function. */
- pl_tuple = SearchSysCache(PROCOID,
- ObjectIdGetDatum(fcinfo->flinfo->fn_oid), 0, 0, 0);
+ pl_tuple = SearchSysCache1(PROCOID,
+ ObjectIdGetDatum(fcinfo->flinfo->fn_oid));
if (!HeapTupleIsValid(pl_tuple))
elog(ERROR, "cache lookup failed for function %u",
fcinfo->flinfo->fn_oid);
@@ -113,6 +117,7 @@ plsample_func_handler(PG_FUNCTION_ARGS)
* a base for the function validation and execution.
*/
pl_struct = (Form_pg_proc) GETSTRUCT(pl_tuple);
+ prolang = pl_struct->prolang;
proname = pstrdup(NameStr(pl_struct->proname));
ret = SysCacheGetAttr(PROCOID, pl_tuple, Anum_pg_proc_prosrc, &isnull);
if (isnull)
@@ -131,26 +136,92 @@ plsample_func_handler(PG_FUNCTION_ARGS)
"PL/Sample function",
ALLOCSET_SMALL_SIZES);
+ /*
+ * This array of FmgrInfo will hold, for each supplied input argument,
+ * the resolved output-to-text function for that argument's type.
+ * fcinfo->nargs comes from the caller, and reflects the number of input
+ * arguments supplied in this call.
+ */
arg_out_func = (FmgrInfo *) palloc0(fcinfo->nargs * sizeof(FmgrInfo));
+
+ /*
+ * Obtain the types, names, and modes (IN/OUT/INOUT/...) for all of
+ * the function's statically declared arguments, returning their number
+ * (giving the size of each result array). numargs can be larger than
+ * fcinfo->nargs (which only reflects IN arguments). numargs can also be
+ * smaller than fcinfo->nargs (in the case of a function declared
+ * VARIADIC "any", but it is rare for a PL to support that).
+ */
numargs = get_func_arg_info(pl_tuple, &argtypes, &argnames, &argmodes);
/*
+ * For now, PL/Sample does not support any argument with mode other than IN.
+ * When mode is IN for every argument, argmodes will be NULL, so report
+ * an error if it is not. To more helpfully report the error at the time
+ * of declaration, a validator function can check this too.
+ */
+ if ( argmodes != NULL )
+ ereport(ERROR,
+ (errmsg("only IN arguments supported for PL/Sample")));
+
+ /*
+ * Obtain the types for which transforms should be applied.
+ * For a PL that does not support transforms, it could be considerate to
+ * report an error here (or earlier, in a validator) if trftypes is not NIL,
+ * rather than simply ignoring any transforms that might be mistakenly
+ * declared
+ */
+ trftypes = get_call_trftypes(pl_tuple);
+
+ /*
* Iterate through all of the function arguments, printing each input
* value.
*/
for (int i = 0; i < numargs; i++)
{
Oid argtype = pl_struct->proargtypes.values[i];
+ Oid fromsql;
char *value;
+ /*
+ * Get the Oid of the "from SQL" transform function if this arg's type
+ * is one of those to which transforms should be applied, or InvalidOid
+ * if not.
+ */
+ fromsql = get_transform_fromsql(argtype, prolang, trftypes);
+
type_tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(argtype));
if (!HeapTupleIsValid(type_tuple))
elog(ERROR, "cache lookup failed for type %u", argtype);
type_struct = (Form_pg_type) GETSTRUCT(type_tuple);
+
+ /*
+ * Look up the type's output function and save the details
+ * in arg_out_func[i].
+ */
fmgr_info_cxt(type_struct->typoutput, &(arg_out_func[i]), proc_cxt);
+
ReleaseSysCache(type_tuple);
+ /*
+ * If a transform function was found for this arg, use it instead of
+ * the default conversion below. In this example, "use it" means
+ * reporting the transform function's name. A real PL would, of course,
+ * apply that function to the argument value and use the result.
+ */
+ if ( OidIsValid(fromsql) )
+ {
+ ereport(NOTICE,
+ (errmsg("argument: %d; name: %s; transformed with: %s",
+ i, argnames[i], format_procedure(fromsql))));
+ continue;
+ }
+
+ /*
+ * No transform, so apply the default conversion that calls the type's
+ * output function to obtain a string.
+ */
value = OutputFunctionCall(&arg_out_func[i], fcinfo->args[i].value);
ereport(NOTICE,
(errmsg("argument: %d; name: %s; value: %s",
@@ -172,6 +243,15 @@ plsample_func_handler(PG_FUNCTION_ARGS)
if (prorettype != TEXTOID)
PG_RETURN_NULL();
+ tosql = get_transform_tosql(prorettype, prolang, trftypes);
+
+ if ( OidIsValid(tosql) )
+ {
+ ereport(NOTICE,
+ (errmsg("return value: transformed with: %s",
+ format_procedure(tosql))));
+ }
+
type_tuple = SearchSysCache1(TYPEOID,
ObjectIdGetDatum(prorettype));
if (!HeapTupleIsValid(type_tuple))
diff --git a/src/test/modules/plsample/sql/plsample.sql b/src/test/modules/plsample/sql/plsample.sql
index bf0fdda..9310cac 100644
--- a/src/test/modules/plsample/sql/plsample.sql
+++ b/src/test/modules/plsample/sql/plsample.sql
@@ -13,3 +13,21 @@ AS $$
Example of source with void result.
$$ LANGUAGE plsample;
SELECT plsample_result_void('{foo, bar, hoge}');
+
+/*
+ * Create a bogus transform using a couple existing functions that
+ * happen to have the right parameter and return types. Calamity does
+ * not ensue, because plsample will merely announce what transform functions
+ * it "would" apply, and not really call them.
+ */
+CREATE TRANSFORM FOR text LANGUAGE plsample (
+ FROM SQL WITH FUNCTION prsd_lextype, TO SQL WITH FUNCTION textrecv
+);
+CREATE OR REPLACE FUNCTION
+ plsample_result_text(a1 numeric, a2 text, a3 integer[])
+RETURNS text
+TRANSFORM FOR TYPE text
+AS $$
+ Example of source with text result.
+$$ LANGUAGE plsample;
+SELECT plsample_result_text(1.23, 'abc', '{4, 5, 6}');
--
2.7.3
--------------070002080407000808050907--
view thread (12+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: chap@anastigmatix.net
Subject: Re: [PATCH 4/4] PL/Sample "with" TRANSFORM FOR TYPE
In-Reply-To: <no-message-id-651326@localhost>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox