On 2022-02-22 12:59, Chapman Flack wrote:
It would have been painful to write documentation of get_func_trftypes
saying its result isn't what get_transform_{from.to}sql expect, so
patch 1 does add a get_call_trftypes that returns a List *.

Patch 2 then updates the docs as discussed in this thread. It turned out
plhandler.sgml was kind of a long monolith of text even before adding
transform information, so I broke it into sections first. This patch adds
the section markup without reindenting, so the changes aren't obscured.

The chapter had also fallen behind the introduction of procedures, so
I have changed many instances of 'function' to the umbrella term
'routine'.

Patch 3 simply reindents for the new section markup and rewraps.
Whitespace only.

Patch 4 updates src/test/modules/plsample to demonstrate handling of
transforms (and to add some more comments generally).

Here is a rebase.
From 9c726423d47a114a82ca703c0e03a62e3d74f1f6 Mon Sep 17 00:00:00 2001
From: Chapman Flack <c...@anastigmatix.net>
Date: Mon, 21 Feb 2022 20:56:28 -0500
Subject: [PATCH v2 1/4] Warmup: add a get_call_trftypes function

The existing get_func_trftypes function produces an Oid[], where
both existing get_transform_{from,to}sql functions that depend
on the result expect a List*.

Rather than writing documentation awkwardly describing functions
that won't play together, add a get_call_trftypes function that
returns List*. (The name get_call_... to distinguish from
get_func_... follows the naming used in funcapi.h for a function
returning information about either a function or a procedure.)
---
 src/backend/utils/cache/lsyscache.c | 18 ++++++++++++++++++
 src/include/funcapi.h               |  5 +++++
 src/include/utils/lsyscache.h       |  1 +
 3 files changed, 24 insertions(+)

diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 1b7e11b..3cd94db 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2117,6 +2117,24 @@ get_transform_tosql(Oid typid, Oid langid, List *trftypes)
 		return InvalidOid;
 }
 
+/*
+ * get_call_trftypes
+ *
+ *		A helper function that does not itself query the transform cache, but
+ *		constructs the transform-type List expected by the functions above.
+ */
+List *
+get_call_trftypes(HeapTuple procTup)
+{
+	Datum		protrftypes;
+	bool		isNull;
+
+	protrftypes = SysCacheGetAttr(PROCOID, procTup,
+								  Anum_pg_proc_protrftypes,
+								  &isNull);
+	return isNull ?  NIL : oid_array_to_list(protrftypes);
+}
+
 
 /*				---------- TYPE CACHE ----------						 */
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index dc3d819..70c3e13 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -175,7 +175,12 @@ extern int	get_func_arg_info(HeapTuple procTup,
 extern int	get_func_input_arg_names(Datum proargnames, Datum proargmodes,
 									 char ***arg_names);
 
+/*
+ * A deprecated earlier version of get_call_trftypes (in lsyscache.h).
+ * That version produces a List, which is the form downstream functions expect.
+ */
 extern int	get_func_trftypes(HeapTuple procTup, Oid **p_trftypes);
+
 extern char *get_func_result_name(Oid functionId);
 
 extern TupleDesc build_function_result_tupdesc_d(char prokind,
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index b8dd27d..93b19e7 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -139,6 +139,7 @@ extern char get_rel_relkind(Oid relid);
 extern bool get_rel_relispartition(Oid relid);
 extern Oid	get_rel_tablespace(Oid relid);
 extern char get_rel_persistence(Oid relid);
+extern List *get_call_trftypes(HeapTuple procTup);
 extern Oid	get_transform_fromsql(Oid typid, Oid langid, List *trftypes);
 extern Oid	get_transform_tosql(Oid typid, Oid langid, List *trftypes);
 extern bool get_typisdefined(Oid typid);
-- 
2.7.3

From 7fb3f8971c4c573903b1d08bcbeb126e31a6544c Mon Sep 17 00:00:00 2001
From: Chapman Flack <c...@anastigmatix.net>
Date: Mon, 21 Feb 2022 20:59:32 -0500
Subject: [PATCH v2 2/4] Update PL handler implementation docs

The original purpose was to add information on support for
CREATE TRANSFORM (which must be explicitly coded in any PL
implementation intending to support it). But the plhandler
section was about as long as a monolith of text ought to be,
even before adding transform information, so reorganized
first into sections.

Front-loaded with short descriptions of the three possible
functions (call handler, validator, inline handler) registered
with CREATE LANGUAGE. The latter two were afterthoughts in historical
sequence, but the docs don't need to present them that way.

The section had also fallen behind the introduction of procedures,
so updated to generally use the umbrella term 'routine' in place
of 'function'.

New section markup added here without indentation change, to avoid
obscuring changes. A follow-on commit will reindent and rewrap.
---
 doc/src/sgml/event-trigger.sgml        |  16 ++
 doc/src/sgml/plhandler.sgml            | 400 ++++++++++++++++++++++++++-------
 doc/src/sgml/ref/create_transform.sgml |  65 ++++--
 doc/src/sgml/xfunc.sgml                |   2 +-
 4 files changed, 393 insertions(+), 90 deletions(-)

diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml
index 9c66f97..14ec70a 100644
--- a/doc/src/sgml/event-trigger.sgml
+++ b/doc/src/sgml/event-trigger.sgml
@@ -670,6 +670,14 @@
         <entry align="left"></entry>
        </row>
        <row>
+        <entry align="left"><literal>CREATE TRANSFORM</literal></entry>
+        <entry align="center"><literal>X</literal></entry>
+        <entry align="center"><literal>X</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
         <entry align="left"><literal>CREATE TRIGGER</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
@@ -958,6 +966,14 @@
         <entry align="left"></entry>
        </row>
        <row>
+        <entry align="left"><literal>DROP TRANSFORM</literal></entry>
+        <entry align="center"><literal>X</literal></entry>
+        <entry align="center"><literal>X</literal></entry>
+        <entry align="center"><literal>X</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
         <entry align="left"><literal>DROP TRIGGER</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
diff --git a/doc/src/sgml/plhandler.sgml b/doc/src/sgml/plhandler.sgml
index 40ee59d..95e8515 100644
--- a/doc/src/sgml/plhandler.sgml
+++ b/doc/src/sgml/plhandler.sgml
@@ -1,7 +1,7 @@
 <!-- doc/src/sgml/plhandler.sgml -->
 
  <chapter id="plhandler">
-   <title>Writing a Procedural Language Handler</title>
+   <title>Implementing a new Procedural Language</title>
 
    <indexterm zone="plhandler">
     <primary>procedural language</primary>
@@ -9,52 +9,117 @@
    </indexterm>
 
    <para>
-    All calls to functions that are written in a language other than
-    the current <quote>version 1</quote> interface for compiled
-    languages (this includes functions in user-defined procedural languages
-    and functions written in SQL) go through a <firstterm>call handler</firstterm>
-    function for the specific language.  It is the responsibility of
-    the call handler to execute the function in a meaningful way, such
-    as by interpreting the supplied source text.  This chapter outlines
-    how a new procedural language's call handler can be written.
+    To make a new procedural language available in
+    <productname>PostgreSQL</productname>, at least one dedicated handler
+    function, and optionally one or two others, must be written and then named
+    in a <xref linkend="sql-createlanguage"/> command. They are:
+    <variablelist>
+     <varlistentry>
+      <term>call handler</term>
+      <listitem>
+       <para>
+        (Required.) Responsible for executing
+        <glossterm linkend="glossary-routine">routines</glossterm>
+        (<glossterm linkend="glossary-function">functions</glossterm> or
+        <glossterm linkend="glossary-procedure">procedures</glossterm>) defined
+        in the procedural language.
+       </para>
+      </listitem>
+     </varlistentry>
+     <varlistentry>
+      <term>validator</term>
+      <listitem>
+       <para>
+        (Optional.) If provided, this will be called whenever a routine using
+        the procedural language has been created or updated, and should check
+        the definition to report any errors detectable at that time.
+       </para>
+      </listitem>
+     </varlistentry>
+     <varlistentry>
+      <term>inline handler</term>
+      <listitem>
+       <para>
+        (Optional.) If the procedural language can be used inline in
+        a <xref linkend="sql-do"/> statement, this handler must be provided,
+        and is responsible for executing such inline code.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
    </para>
 
    <para>
-    The call handler for a procedural language is a
-    <quote>normal</quote> function that must be written in a compiled
-    language such as C, using the version-1 interface, and registered
-    with <productname>PostgreSQL</productname> as taking no arguments
+    This chapter outlines how a new procedural language's handlers can be
+    written.
+   </para>
+
+   <sect1 id="plhandler-call">
+    <title>Call handler function</title>
+
+   <para>
+    Every routine defined with a language name other than
+    <literal>internal</literal> (as defined in <xref linkend="xfunc-internal"/>)
+    or <literal>c</literal> (<xref linkend="xfunc-c"/>) will be called by
+    invoking the procedural language's call handler.
+    <footnote>
+     <para>
+      This is true even of routines with language name <literal>sql</literal>,
+      though, as a special case, that call handler has no entry in the system
+      catalogs.
+     </para>
+    </footnote>
+    It is the responsibility of
+    the call handler to execute the routine in a meaningful way, such
+    as by interpreting the supplied source text.
+   </para>
+
+   <para>
+    The call handler is a <quote>normal</quote> user-defined function that must
+    be declared to <productname>PostgreSQL</productname> as taking no arguments
     and returning the type <type>language_handler</type>.  This
     special pseudo-type identifies the function as a call handler and
     prevents it from being called directly in SQL commands.
-    For more details on C language calling conventions and dynamic loading,
-    see <xref linkend="xfunc-c"/>.
+    This handler must not require a call handler of its own, which makes
+    the predefined languages <literal>internal</literal> or <literal>c</literal>
+    the only choices for the handler's own declaration.
+    Typically, it will be a loadable function in language
+    <literal>c</literal>, as described in <xref linkend="xfunc-c"/>.
+    <footnote>
+     <para>
+      It may be implemented in a language other than C, as long as it can be
+      built into a loadable object with compatible calling conventions.
+     </para>
+    </footnote>
    </para>
 
    <para>
     The call handler is called in the same way as any other function:
     It receives a pointer to a
     <structname>FunctionCallInfoBaseData</structname> <type>struct</type> containing
-    argument values and information about the called function, and it
+    argument values and information about the called routine, and it
     is expected to return a <type>Datum</type> result (and possibly
     set the <structfield>isnull</structfield> field of the
     <structname>FunctionCallInfoBaseData</structname> structure, if it wishes
     to return an SQL null result).  The difference between a call
-    handler and an ordinary callee function is that the
+    handler and an ordinary callee is that the
     <structfield>flinfo-&gt;fn_oid</structfield> field of the
     <structname>FunctionCallInfoBaseData</structname> structure will contain
-    the OID of the actual function to be called, not of the call
+    the OID of the actual routine to be called, not of the call
     handler itself.  The call handler must use this field to determine
-    which function to execute.  Also, the passed argument list has
-    been set up according to the declaration of the target function,
+    which routine to execute.  Also, the passed argument list has
+    been set up according to the declaration of the target routine,
     not of the call handler.
    </para>
 
    <para>
-    It's up to the call handler to fetch the entry of the function from the
-    <classname>pg_proc</classname> system catalog and to analyze the argument
-    and return types of the called function. The <literal>AS</literal> clause from the
-    <command>CREATE FUNCTION</command> command for the function will be found
+    It's up to the call handler to fetch the routine's defining
+    <classname>pg_proc</classname> row from the system catalog cache
+    to determine what to execute, what parameter and return types are expected,
+    and so on.
+    The <literal>AS</literal> clause from the
+    <command>CREATE FUNCTION</command> or <command>CREATE PROCEDURE</command>
+    command for the routine will be found
     in the <literal>prosrc</literal> column of the
     <classname>pg_proc</classname> row. This is commonly source
     text in the procedural language, but in theory it could be something else,
@@ -63,12 +128,147 @@
    </para>
 
    <para>
-    Often, the same function is called many times per SQL statement.
+    The handler may also examine the passed
+    <structname>FunctionCallInfoBaseData</structname> structure for information
+    on the context of the call. If the procedural language will support
+    returning sets, the structure may contain a pointer to a
+    <structname>ReturnSetInfo</structname> structure for use as described in
+    <xref linkend="xfunc-c-return-set"/>. If the language will support triggers
+    or event triggers, the structure may hold a pointer to one of the structures
+    described in <xref linkend="trigger-interface"/> or
+    <xref linkend="event-trigger-interface"/>, and the procedural language
+    should provide some way for the called function to use the information
+    they carry.
+   </para>
+
+   <sect2 id="plhandler-call-args">
+    <title>Parameter and return type resolution</title>
+
+    <para>
+     A routine's statically-declared parameter types (and, for a function,
+     return type) are found in the <literal>proargtypes</literal> and
+     <literal>prorettype</literal> columns of the <classname>pg_proc</classname>
+     row.
+     If a routine has <literal>OUT</literal> parameters, those types are
+     included in the <literal>proallargtypes</literal> column, and their names
+     in <literal>proargnames</literal>.
+     Convenience functions declared in <filename>funcapi.h</filename> are
+     available for extracting that information.
+    </para>
+
+    <para>
+     The statically-declared types may include polymorphic types that need
+     to be resolved according to the actual types present at the call site,
+     as described in <xref linkend="xfunc-c-polymorphism"/>.
+    </para>
+
+   </sect2>
+
+   <sect2 id="plhandler-call-types">
+    <title>Mapping to procedural language types</title>
+
+    <para>
+     Once the <productname>PostgreSQL</productname> types of any parameters
+     and results have been resolved, the handler must determine how it will
+     map their values to and from suitable types that exist in the procedural
+     language.
+    </para>
+
+    <para>
+     The designer of a procedural language will typically document what types
+     will be supported and how they will be mapped, which could be as simple
+     as using every type's text input/output format to map it to the target
+     language's string type, or could directly map many types to corresponding
+     ones the target language provides. The handler function will implement
+     those rules.
+    </para>
+
+    <sect3 id="plhandler-call-types-transform">
+     <title>Type transforms</title>
+
+     <para>
+      Because <productname>PostgreSQL</productname> is extensible, and
+      an extension can easily supply new types, a procedural language handler
+      may encounter types it has no predefined mappings for, or only an awkward
+      default mapping such as to a text string. A procedural language can be
+      designed so that its type mappings are also extensible, and an extension
+      can add mappings between new <productname>PostgreSQL</productname> types
+      and suitable types in the target language.
+     </para>
+
+     <para>
+      One mechanism <productname>PostgreSQL</productname> provides that may be
+      used for that purpose is <xref linkend="sql-createtransform"/>.
+      The command associates a <productname>PostgreSQL</productname> type and
+      a specific procedural language with a pair of functions to handle the
+      mapping of that type to a corresponding procedural language type and back.
+     </para>
+
+     <para>
+      For a procedural language to support transforms, its call handler is
+      responsible for consulting the <literal>protrftypes</literal> column of
+      a routine's <classname>pg_proc</classname> row to determine which types
+      should have transforms applied.
+      A convenience function <function>get_call_trftypes</function> is
+      available.
+      The call handler must then resolve the <quote>from SQL</quote> function
+      for each affected parameter type, and the <quote>to SQL</quote> function
+      for any affected result.
+      It may use the <function>get_transform_fromsql</function> and
+      <function>get_transform_tosql</function> functions for that.
+     </para>
+
+     <para>
+      The handler must then apply the proper <quote>from SQL</quote> functions
+      to all affected inputs (including elements within array or composite
+      types) and, after calling the target routine, apply the proper
+      <quote>to SQL</quote> functions similarly to any results.
+      If the target routine might interact with the database using SPI,
+      the handler may arrange for the requested transforms to be applied
+      in those operations as well.
+     </para>
+
+     <para>
+      Because the procedural language implementation, and not
+      <productname>PostgreSQL</productname> itself, is responsible for calling
+      the transform functions, it is free to define what it will pass as the
+      parameter to each function (declared as <type>internal</type> for both),
+      and how it will interpret the result (also declared <type>internal</type>)
+      of the <quote>from SQL</quote> function. Effectively, each procedural
+      language's implementation defines the API that must be adhered to
+      by any author of transforms for that language.
+     </para>
+
+     <para>
+      A procedural language might impose limits on where and how it will apply
+      transforms (such as on array or domain types). The
+      <function>get_transform_fromsql</function> and
+      <function>get_transform_tosql</function> functions mentioned above
+      consider each type only shallowly, and will not, for example, return
+      a transform function for a domain type if only its base type was listed in
+      the <literal>TRANSFORM</literal> clause.
+      If a procedural language's call handler does not implement transforms
+      at all, no <literal>TRANSFORM</literal> clause will have any effect
+      for routines declared in that language.
+      The language's validator function can be used to give immediate feedback
+      if a routine is declared with <literal>TRANSFORM</literal> clauses
+      the implementation cannot support.
+     </para>
+
+    </sect3>
+
+   </sect2>
+
+   <sect2 id="plhandler-call-cache">
+    <title>Caching resolved routine information</title>
+
+   <para>
+    Often, the same routine is called many times per SQL statement.
     A call handler can avoid repeated lookups of information about the
-    called function by using the
+    called routine by using the
     <structfield>flinfo-&gt;fn_extra</structfield> field.  This will
     initially be <symbol>NULL</symbol>, but can be set by the call handler to point at
-    information about the called function.  On subsequent calls, if
+    information about the called routine.  On subsequent calls, if
     <structfield>flinfo-&gt;fn_extra</structfield> is already non-<symbol>NULL</symbol>
     then it can be used and the information lookup step skipped.  The
     call handler must make sure that
@@ -81,59 +281,58 @@
     normally have the same lifespan as the
     <structname>FmgrInfo</structname> itself.  But the handler could
     also choose to use a longer-lived memory context so that it can cache
-    function definition information across queries.
+    routine definition information across queries.
    </para>
 
    <para>
-    When a procedural-language function is invoked as a trigger, no arguments
-    are passed in the usual way, but the
-    <structname>FunctionCallInfoBaseData</structname>'s
-    <structfield>context</structfield> field points at a
-    <structname>TriggerData</structname> structure, rather than being <symbol>NULL</symbol>
-    as it is in a plain function call.  A language handler should
-    provide mechanisms for procedural-language functions to get at the trigger
-    information.
+    If the handler supports returning sets, and uses the ValuePerCall mode
+    helper macros described in <xref linkend="xfunc-c-return-set"/>, it must
+    not use <structfield>fn_extra</structfield> during set-returning calls.
+    The helper macros use that field for their own purposes. After
+    <literal>SRF_FIRSTCALL_INIT</literal> has been called, the field will point
+    to a <structname>FuncCallContext</structname> structure, which has
+    a <structfield>user_fctx</structfield> field that can be used similarly,
+    but only through the sequence of calls returning one set result.
    </para>
 
-   <para>
-    A template for a procedural-language handler written as a C extension is
-    provided in <literal>src/test/modules/plsample</literal>.  This is a
-    working sample demonstrating one way to create a procedural-language
-    handler, process parameters, and return a value.
-   </para>
+   </sect2>
 
-   <para>
-    Although providing a call handler is sufficient to create a minimal
-    procedural language, there are two other functions that can optionally
-    be provided to make the language more convenient to use.  These
-    are a <firstterm>validator</firstterm> and an
-    <firstterm>inline handler</firstterm>.  A validator can be provided
-    to allow language-specific checking to be done during
-    <xref linkend="sql-createfunction"/>.
-    An inline handler can be provided to allow the language to support
-    anonymous code blocks executed via the <xref linkend="sql-do"/> command.
-   </para>
+   </sect1>
+
+   <sect1 id="plhandler-validator">
+    <title>Validator function</title>
 
    <para>
     If a validator is provided by a procedural language, it
     must be declared as a function taking a single parameter of type
     <type>oid</type>.  The validator's result is ignored, so it is customarily
-    declared to return <type>void</type>.  The validator will be called at
-    the end of a <command>CREATE FUNCTION</command> command that has created
-    or updated a function written in the procedural language.
-    The passed-in OID is the OID of the function's <classname>pg_proc</classname>
+    declared to return <type>void</type>.
+    The validator itself may be written in any procedural language able to
+    receive an <type>oid</type>-typed parameter and query system catalogs.
+   </para>
+
+   <para>
+    The validator will be called at
+    the end of a <command>CREATE FUNCTION</command> or
+    <command>CREATE PROCEDURE</command> command that has created
+    or updated a routine written in the procedural language.
+    The passed-in OID is the OID of the routine's <classname>pg_proc</classname>
     row.  The validator must fetch this row in the usual way, and do
     whatever checking is appropriate.
+   </para>
+
+   <para>
     First, call <function>CheckFunctionValidatorAccess()</function> to diagnose
     explicit calls to the validator that the user could not achieve through
-    <command>CREATE FUNCTION</command>.  Typical checks then include verifying
-    that the function's argument and result types are supported by the
-    language, and that the function's body is syntactically correct
-    in the language.  If the validator finds the function to be okay,
+    <command>CREATE FUNCTION</command> or <command>CREATE PROCEDURE</command>.
+    Typical checks then include verifying
+    that the routine's argument and result types are supported by the
+    language, and that the routine's body is syntactically correct
+    in the language.  If the validator finds the routine to be okay,
     it should just return.  If it finds an error, it should report that
     via the normal <function>ereport()</function> error reporting mechanism.
     Throwing an error will force a transaction rollback and thus prevent
-    the incorrect function definition from being committed.
+    the incorrect routine definition from being committed.
    </para>
 
    <para>
@@ -143,35 +342,72 @@
     language provides for code execution at compilation time, the validator
     must suppress checks that would induce such execution.  In particular,
     this parameter is turned off by <application>pg_dump</application> so that it can
-    load procedural language functions without worrying about side effects or
-    dependencies of the function bodies on other database objects.
+    load procedural language routines without worrying about side effects or
+    dependencies of the routine bodies on other database objects.
     (Because of this requirement, the call handler should avoid
-    assuming that the validator has fully checked the function.  The point
+    assuming that the validator has fully checked the routine.  The point
     of having a validator is not to let the call handler omit checks, but
     to notify the user immediately if there are obvious errors in a
-    <command>CREATE FUNCTION</command> command.)
+    <command>CREATE FUNCTION</command> or <command>CREATE PROCEDURE</command>
+    command.)
+   </para>
+
+   <para>
     While the choice of exactly what to check is mostly left to the
     discretion of the validator function, note that the core
-    <command>CREATE FUNCTION</command> code only executes <literal>SET</literal> clauses
-    attached to a function when <varname>check_function_bodies</varname> is on.
+    <command>CREATE FUNCTION</command> and <command>CREATE PROCEDURE</command>
+    code only executes <literal>SET</literal> clauses
+    attached to a routine when <varname>check_function_bodies</varname> is on.
     Therefore, checks whose results might be affected by GUC parameters
     definitely should be skipped when <varname>check_function_bodies</varname> is
     off, to avoid false failures when reloading a dump.
    </para>
 
    <para>
-    If an inline handler is provided by a procedural language, it
+    If a language's call handler does not apply parameter and return type
+    transforms, then no <literal>TRANSFORM</literal> clause in a routine
+    declaration will have any effect. To provide immediate feedback if a
+    declaration contains such a clause, the validator can report a suitable
+    error whenever the <literal>protrftypes</literal> column of the routine's
+    <classname>pg_proc</classname> row is non-null.
+   </para>
+
+   </sect1>
+
+   <sect1 id="plhandler-inline">
+    <title>Inline handler function</title>
+
+   <para>
+    If this handler is provided by a procedural language, it
     must be declared as a function taking a single parameter of type
-    <type>internal</type>.  The inline handler's result is ignored, so it is
-    customarily declared to return <type>void</type>.  The inline handler
+    <type>internal</type>, which will be a pointer
+    to an <structname>InlineCodeBlock</structname> struct when the handler
+    is called.  The result is ignored, so the return type is customarily
+    declared as <type>void</type>.
+    The inline handler itself may be written in any procedural language that
+    permits declaring an <type>internal</type> parameter with a suitable
+    language binding for accessing it as an
+    <structname>InlineCodeBlock</structname> struct.
+   </para>
+
+   <para>
+    The inline handler
     will be called when a <command>DO</command> statement is executed specifying
-    the procedural language.  The parameter actually passed is a pointer
-    to an <structname>InlineCodeBlock</structname> struct, which contains information
+    the procedural language.  The <structname>InlineCodeBlock</structname>
+    struct contains information
     about the <command>DO</command> statement's parameters, in particular the
-    text of the anonymous code block to be executed.  The inline handler
-    should execute this code and return.
+    text of the anonymous code block to be executed.
+    It also contains the OID of the intended procedural language and whether
+    that procedural language is declared as <literal>TRUSTED</literal>, useful
+    if a single inline handler is supporting more than one procedural language.
+    The inline handler should execute the code block and return.
    </para>
 
+   </sect1>
+
+   <sect1 id="plhandler-packaging">
+    <title>Packaging the language handlers</title>
+
    <para>
     It's recommended that you wrap all these function declarations,
     as well as the <command>CREATE LANGUAGE</command> command itself, into
@@ -181,6 +417,18 @@
     extensions.
    </para>
 
+   </sect1>
+
+   <sect1 id="plhandler-examples">
+    <title>Example code</title>
+
+   <para>
+    A template for a procedural-language handler written as a C extension is
+    provided in <literal>src/test/modules/plsample</literal>.  This is a
+    working sample demonstrating one way to create a procedural-language
+    handler, process parameters, and return a value.
+   </para>
+
    <para>
     The procedural languages included in the standard distribution
     are good references when trying to write your own language handler.
@@ -189,4 +437,6 @@
     reference page also has some useful details.
    </para>
 
+   </sect1>
+
  </chapter>
diff --git a/doc/src/sgml/ref/create_transform.sgml b/doc/src/sgml/ref/create_transform.sgml
index 34bdc60..e1dc79a 100644
--- a/doc/src/sgml/ref/create_transform.sgml
+++ b/doc/src/sgml/ref/create_transform.sgml
@@ -22,8 +22,8 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 CREATE [ OR REPLACE ] TRANSFORM FOR <replaceable>type_name</replaceable> LANGUAGE <replaceable>lang_name</replaceable> (
-    FROM SQL WITH FUNCTION <replaceable>from_sql_function_name</replaceable> [ (<replaceable>argument_type</replaceable> [, ...]) ],
-    TO SQL WITH FUNCTION <replaceable>to_sql_function_name</replaceable> [ (<replaceable>argument_type</replaceable> [, ...]) ]
+    FROM SQL WITH FUNCTION <replaceable>from_sql_function_name</replaceable> [ (<type>internal</type>) ],
+    TO SQL WITH FUNCTION <replaceable>to_sql_function_name</replaceable> [ (<type>internal</type>) ]
 );
 </synopsis>
  </refsynopsisdiv>
@@ -80,6 +80,19 @@ CREATE [ OR REPLACE ] TRANSFORM FOR <replaceable>type_name</replaceable> LANGUAG
    have <literal>EXECUTE</literal> privilege on the from-SQL and to-SQL
    functions, if specified.
   </para>
+
+  <para>
+   Typically, <command>CREATE TRANSFORM</command> will be used to register
+   transforms developed by others (or, even more typically, they will be
+   packaged in an extension, and <command>CREATE EXTENSION</command> will
+   do the registration).
+   To develop new transform functions for a given procedural language, you must
+   be familiar with its implementation, which determines whether it supports
+   transforms at all, and defines the details, such as what is expected for
+   the return and parameter types declared <type>internal</type>.
+   To develop a procedural language with support for transforms, see
+   <xref linkend="plhandler-call-types-transform"/>.
+  </para>
  </refsect1>
 
  <refsect1>
@@ -107,18 +120,19 @@ CREATE [ OR REPLACE ] TRANSFORM FOR <replaceable>type_name</replaceable> LANGUAG
     </varlistentry>
 
     <varlistentry>
-     <term><literal><replaceable>from_sql_function_name</replaceable>[(<replaceable>argument_type</replaceable> [, ...])]</literal></term>
+     <term><literal><replaceable>from_sql_function_name</replaceable>[(<type>internal</type>)]</literal></term>
 
      <listitem>
       <para>
        The name of the function for converting the type from the SQL
        environment to the language.  It must take one argument of
        type <type>internal</type> and return type <type>internal</type>.  The
-       actual argument will be of the type for the transform, and the function
-       should be coded as if it were.  (But it is not allowed to declare an
-       SQL-level function returning <type>internal</type> without at
-       least one argument of type <type>internal</type>.)  The actual return
-       value will be something specific to the language implementation.
+       actual argument will be supplied by the language handler. It will
+       typically be of the type for the transform (even though declared
+       <type>internal</type>), but when writing transforms for a particular
+       language, its implementation should be consulted for the details.
+       The actual return
+       value will also be something specific to the language implementation.
        If no argument list is specified, the function name must be unique in
        its schema.
       </para>
@@ -126,7 +140,7 @@ CREATE [ OR REPLACE ] TRANSFORM FOR <replaceable>type_name</replaceable> LANGUAG
     </varlistentry>
 
     <varlistentry>
-     <term><literal><replaceable>to_sql_function_name</replaceable>[(<replaceable>argument_type</replaceable> [, ...])]</literal></term>
+     <term><literal><replaceable>to_sql_function_name</replaceable>[(<type>internal</type>)]</literal></term>
 
      <listitem>
       <para>
@@ -192,11 +206,34 @@ CREATE TRANSFORM FOR hstore LANGUAGE plpython3u (
   <title>Compatibility</title>
 
   <para>
-   This form of <command>CREATE TRANSFORM</command> is a
-   <productname>PostgreSQL</productname> extension.  There is a <command>CREATE
-   TRANSFORM</command> command in the <acronym>SQL</acronym> standard, but it
-   is for adapting data types to client languages.  That usage is not supported
-   by <productname>PostgreSQL</productname>.
+   There is a <command>CREATE TRANSFORM</command> command in the
+   <acronym>SQL</acronym> standard.
+   <literal>LANGUAGE <replaceable>lang_name</replaceable></literal> is a
+   <productname>PostgreSQL</productname> extension.
+   A transform in the <acronym>SQL</acronym> standard may be created only for
+   a <quote>user-defined</quote> type, and transforms it to a standard
+   <acronym>SQL</acronym> type that is expected to have cross-language support.
+   A <productname>PostgreSQL</productname> transform can be created for
+   any type, and transforms it directly to a language-specific type.
+  </para>
+
+  <para>
+   A transform in the <acronym>SQL</acronym> standard is applied
+   for server-side routines and also for embedded-<acronym>SQL</acronym> client
+   languages. In <productname>PostgreSQL</productname>, a transform is only
+   applied for server-side routines.
+  </para>
+
+  <para>
+   In the <acronym>SQL</acronym> standard, a transform group (one pair of
+   from-<acronym>SQL</acronym> and to-<acronym>SQL</acronym> functions) may have
+   a group name, allowing multiple transforms to exist for the same type, and
+   each routine may declare which one to apply, by giving its name.
+   Transform groups are not named in <productname>PostgreSQL</productname>,
+   so only one transform for a given type and procedural language may exist at
+   any time. Routines using that language may choose between two mappings for
+   the type: the built-in default, or the one defined by the single available
+   transform.
   </para>
  </refsect1>
 
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index a347230..df613e6 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -3295,7 +3295,7 @@ CREATE OR REPLACE FUNCTION retcomposite(IN integer, IN integer,
     </para>
    </sect2>
 
-   <sect2>
+   <sect2 id="xfunc-c-polymorphism">
     <title>Polymorphic Arguments and Return Types</title>
 
     <para>
-- 
2.7.3

From 3f8d7bdb785c0a124b11f3131d9c5323717e0b83 Mon Sep 17 00:00:00 2001
From: Chapman Flack <c...@anastigmatix.net>
Date: Mon, 21 Feb 2022 21:21:51 -0500
Subject: [PATCH v2 3/4] Reindent and wrap between added sect1 tags

A whitespace-only commit.
---
 doc/src/sgml/plhandler.sgml | 638 ++++++++++++++++++++++----------------------
 1 file changed, 321 insertions(+), 317 deletions(-)

diff --git a/doc/src/sgml/plhandler.sgml b/doc/src/sgml/plhandler.sgml
index 95e8515..003a6c9 100644
--- a/doc/src/sgml/plhandler.sgml
+++ b/doc/src/sgml/plhandler.sgml
@@ -57,385 +57,389 @@
    <sect1 id="plhandler-call">
     <title>Call handler function</title>
 
-   <para>
-    Every routine defined with a language name other than
-    <literal>internal</literal> (as defined in <xref linkend="xfunc-internal"/>)
-    or <literal>c</literal> (<xref linkend="xfunc-c"/>) will be called by
-    invoking the procedural language's call handler.
-    <footnote>
-     <para>
-      This is true even of routines with language name <literal>sql</literal>,
-      though, as a special case, that call handler has no entry in the system
-      catalogs.
-     </para>
-    </footnote>
-    It is the responsibility of
-    the call handler to execute the routine in a meaningful way, such
-    as by interpreting the supplied source text.
-   </para>
-
-   <para>
-    The call handler is a <quote>normal</quote> user-defined function that must
-    be declared to <productname>PostgreSQL</productname> as taking no arguments
-    and returning the type <type>language_handler</type>.  This
-    special pseudo-type identifies the function as a call handler and
-    prevents it from being called directly in SQL commands.
-    This handler must not require a call handler of its own, which makes
-    the predefined languages <literal>internal</literal> or <literal>c</literal>
-    the only choices for the handler's own declaration.
-    Typically, it will be a loadable function in language
-    <literal>c</literal>, as described in <xref linkend="xfunc-c"/>.
-    <footnote>
-     <para>
-      It may be implemented in a language other than C, as long as it can be
-      built into a loadable object with compatible calling conventions.
-     </para>
-    </footnote>
-   </para>
-
-   <para>
-    The call handler is called in the same way as any other function:
-    It receives a pointer to a
-    <structname>FunctionCallInfoBaseData</structname> <type>struct</type> containing
-    argument values and information about the called routine, and it
-    is expected to return a <type>Datum</type> result (and possibly
-    set the <structfield>isnull</structfield> field of the
-    <structname>FunctionCallInfoBaseData</structname> structure, if it wishes
-    to return an SQL null result).  The difference between a call
-    handler and an ordinary callee is that the
-    <structfield>flinfo-&gt;fn_oid</structfield> field of the
-    <structname>FunctionCallInfoBaseData</structname> structure will contain
-    the OID of the actual routine to be called, not of the call
-    handler itself.  The call handler must use this field to determine
-    which routine to execute.  Also, the passed argument list has
-    been set up according to the declaration of the target routine,
-    not of the call handler.
-   </para>
-
-   <para>
-    It's up to the call handler to fetch the routine's defining
-    <classname>pg_proc</classname> row from the system catalog cache
-    to determine what to execute, what parameter and return types are expected,
-    and so on.
-    The <literal>AS</literal> clause from the
-    <command>CREATE FUNCTION</command> or <command>CREATE PROCEDURE</command>
-    command for the routine will be found
-    in the <literal>prosrc</literal> column of the
-    <classname>pg_proc</classname> row. This is commonly source
-    text in the procedural language, but in theory it could be something else,
-    such as a path name to a file, or anything else that tells the call handler
-    what to do in detail.
-   </para>
-
-   <para>
-    The handler may also examine the passed
-    <structname>FunctionCallInfoBaseData</structname> structure for information
-    on the context of the call. If the procedural language will support
-    returning sets, the structure may contain a pointer to a
-    <structname>ReturnSetInfo</structname> structure for use as described in
-    <xref linkend="xfunc-c-return-set"/>. If the language will support triggers
-    or event triggers, the structure may hold a pointer to one of the structures
-    described in <xref linkend="trigger-interface"/> or
-    <xref linkend="event-trigger-interface"/>, and the procedural language
-    should provide some way for the called function to use the information
-    they carry.
-   </para>
-
-   <sect2 id="plhandler-call-args">
-    <title>Parameter and return type resolution</title>
-
     <para>
-     A routine's statically-declared parameter types (and, for a function,
-     return type) are found in the <literal>proargtypes</literal> and
-     <literal>prorettype</literal> columns of the <classname>pg_proc</classname>
-     row.
-     If a routine has <literal>OUT</literal> parameters, those types are
-     included in the <literal>proallargtypes</literal> column, and their names
-     in <literal>proargnames</literal>.
-     Convenience functions declared in <filename>funcapi.h</filename> are
-     available for extracting that information.
+     Every routine defined with a language name other than
+     <literal>internal</literal> (as defined in
+     <xref linkend="xfunc-internal"/>)
+     or <literal>c</literal> (<xref linkend="xfunc-c"/>) will be called by
+     invoking the procedural language's call handler.
+     <footnote>
+      <para>
+       This is true even of routines with language name <literal>sql</literal>,
+       though, as a special case, that call handler has no entry in the system
+       catalogs.
+      </para>
+     </footnote>
+     It is the responsibility of
+     the call handler to execute the routine in a meaningful way, such
+     as by interpreting the supplied source text.
     </para>
 
     <para>
-     The statically-declared types may include polymorphic types that need
-     to be resolved according to the actual types present at the call site,
-     as described in <xref linkend="xfunc-c-polymorphism"/>.
+     The call handler is a <quote>normal</quote> user-defined function that must
+     be declared to <productname>PostgreSQL</productname> as taking no arguments
+     and returning the type <type>language_handler</type>.  This
+     special pseudo-type identifies the function as a call handler and
+     prevents it from being called directly in SQL commands.
+     This handler must not require a call handler of its own, which makes
+     the predefined languages <literal>internal</literal> or
+     <literal>c</literal> the only choices for the handler's own declaration.
+     Typically, it will be a loadable function in language
+     <literal>c</literal>, as described in <xref linkend="xfunc-c"/>.
+     <footnote>
+      <para>
+       It may be implemented in a language other than C, as long as it can be
+       built into a loadable object with compatible calling conventions.
+      </para>
+     </footnote>
     </para>
 
-   </sect2>
-
-   <sect2 id="plhandler-call-types">
-    <title>Mapping to procedural language types</title>
+    <para>
+     The call handler is called in the same way as any other function:
+     It receives a pointer to a
+     <structname>FunctionCallInfoBaseData</structname> <type>struct</type>
+     containing argument values and information about the called routine, and it
+     is expected to return a <type>Datum</type> result (and possibly
+     set the <structfield>isnull</structfield> field of the
+     <structname>FunctionCallInfoBaseData</structname> structure, if it wishes
+     to return an SQL null result).  The difference between a call
+     handler and an ordinary callee is that the
+     <structfield>flinfo-&gt;fn_oid</structfield> field of the
+     <structname>FunctionCallInfoBaseData</structname> structure will contain
+     the OID of the actual routine to be called, not of the call
+     handler itself.  The call handler must use this field to determine
+     which routine to execute.  Also, the passed argument list has
+     been set up according to the declaration of the target routine,
+     not of the call handler.
+    </para>
 
     <para>
-     Once the <productname>PostgreSQL</productname> types of any parameters
-     and results have been resolved, the handler must determine how it will
-     map their values to and from suitable types that exist in the procedural
-     language.
+     It's up to the call handler to fetch the routine's defining
+     <classname>pg_proc</classname> row from the system catalog cache
+     to determine what to execute, what parameter and return types are expected,
+     and so on.
+     The <literal>AS</literal> clause from the
+     <command>CREATE FUNCTION</command> or <command>CREATE PROCEDURE</command>
+     command for the routine will be found
+     in the <literal>prosrc</literal> column of the
+     <classname>pg_proc</classname> row. This is commonly source
+     text in the procedural language, but in theory it could be something else,
+     such as a path name to a file, or anything else that tells the call handler
+     what to do in detail.
     </para>
 
     <para>
-     The designer of a procedural language will typically document what types
-     will be supported and how they will be mapped, which could be as simple
-     as using every type's text input/output format to map it to the target
-     language's string type, or could directly map many types to corresponding
-     ones the target language provides. The handler function will implement
-     those rules.
+     The handler may also examine the passed
+     <structname>FunctionCallInfoBaseData</structname> structure for information
+     on the context of the call. If the procedural language will support
+     returning sets, the structure may contain a pointer to a
+     <structname>ReturnSetInfo</structname> structure for use as described in
+     <xref linkend="xfunc-c-return-set"/>. If the language will support triggers
+     or event triggers, the structure may hold a pointer to one of
+     the structures described in <xref linkend="trigger-interface"/> or
+     <xref linkend="event-trigger-interface"/>, and the procedural language
+     should provide some way for the called function to use the information
+     they carry.
     </para>
 
-    <sect3 id="plhandler-call-types-transform">
-     <title>Type transforms</title>
+    <sect2 id="plhandler-call-args">
+     <title>Parameter and return type resolution</title>
 
      <para>
-      Because <productname>PostgreSQL</productname> is extensible, and
-      an extension can easily supply new types, a procedural language handler
-      may encounter types it has no predefined mappings for, or only an awkward
-      default mapping such as to a text string. A procedural language can be
-      designed so that its type mappings are also extensible, and an extension
-      can add mappings between new <productname>PostgreSQL</productname> types
-      and suitable types in the target language.
+      A routine's statically-declared parameter types (and, for a function,
+      return type) are found in the <literal>proargtypes</literal> and
+      <literal>prorettype</literal> columns of the
+      <classname>pg_proc</classname> row.
+      If a routine has <literal>OUT</literal> parameters, those types are
+      included in the <literal>proallargtypes</literal> column, and their names
+      in <literal>proargnames</literal>.
+      Convenience functions declared in <filename>funcapi.h</filename> are
+      available for extracting that information.
      </para>
 
      <para>
-      One mechanism <productname>PostgreSQL</productname> provides that may be
-      used for that purpose is <xref linkend="sql-createtransform"/>.
-      The command associates a <productname>PostgreSQL</productname> type and
-      a specific procedural language with a pair of functions to handle the
-      mapping of that type to a corresponding procedural language type and back.
+      The statically-declared types may include polymorphic types that need
+      to be resolved according to the actual types present at the call site,
+      as described in <xref linkend="xfunc-c-polymorphism"/>.
      </para>
 
-     <para>
-      For a procedural language to support transforms, its call handler is
-      responsible for consulting the <literal>protrftypes</literal> column of
-      a routine's <classname>pg_proc</classname> row to determine which types
-      should have transforms applied.
-      A convenience function <function>get_call_trftypes</function> is
-      available.
-      The call handler must then resolve the <quote>from SQL</quote> function
-      for each affected parameter type, and the <quote>to SQL</quote> function
-      for any affected result.
-      It may use the <function>get_transform_fromsql</function> and
-      <function>get_transform_tosql</function> functions for that.
-     </para>
+    </sect2>
 
-     <para>
-      The handler must then apply the proper <quote>from SQL</quote> functions
-      to all affected inputs (including elements within array or composite
-      types) and, after calling the target routine, apply the proper
-      <quote>to SQL</quote> functions similarly to any results.
-      If the target routine might interact with the database using SPI,
-      the handler may arrange for the requested transforms to be applied
-      in those operations as well.
-     </para>
+    <sect2 id="plhandler-call-types">
+     <title>Mapping to procedural language types</title>
 
      <para>
-      Because the procedural language implementation, and not
-      <productname>PostgreSQL</productname> itself, is responsible for calling
-      the transform functions, it is free to define what it will pass as the
-      parameter to each function (declared as <type>internal</type> for both),
-      and how it will interpret the result (also declared <type>internal</type>)
-      of the <quote>from SQL</quote> function. Effectively, each procedural
-      language's implementation defines the API that must be adhered to
-      by any author of transforms for that language.
+      Once the <productname>PostgreSQL</productname> types of any parameters
+      and results have been resolved, the handler must determine how it will
+      map their values to and from suitable types that exist in the procedural
+      language.
      </para>
 
      <para>
-      A procedural language might impose limits on where and how it will apply
-      transforms (such as on array or domain types). The
-      <function>get_transform_fromsql</function> and
-      <function>get_transform_tosql</function> functions mentioned above
-      consider each type only shallowly, and will not, for example, return
-      a transform function for a domain type if only its base type was listed in
-      the <literal>TRANSFORM</literal> clause.
-      If a procedural language's call handler does not implement transforms
-      at all, no <literal>TRANSFORM</literal> clause will have any effect
-      for routines declared in that language.
-      The language's validator function can be used to give immediate feedback
-      if a routine is declared with <literal>TRANSFORM</literal> clauses
-      the implementation cannot support.
+      The designer of a procedural language will typically document what types
+      will be supported and how they will be mapped, which could be as simple
+      as using every type's text input/output format to map it to the target
+      language's string type, or could directly map many types to corresponding
+      ones the target language provides. The handler function will implement
+      those rules.
      </para>
 
-    </sect3>
-
-   </sect2>
-
-   <sect2 id="plhandler-call-cache">
-    <title>Caching resolved routine information</title>
+     <sect3 id="plhandler-call-types-transform">
+      <title>Type transforms</title>
+
+      <para>
+       Because <productname>PostgreSQL</productname> is extensible, and
+       an extension can easily supply new types, a procedural language handler
+       may encounter types it has no predefined mappings for, or only an awkward
+       default mapping such as to a text string. A procedural language can be
+       designed so that its type mappings are also extensible, and an extension
+       can add mappings between new <productname>PostgreSQL</productname> types
+       and suitable types in the target language.
+      </para>
+
+      <para>
+       One mechanism <productname>PostgreSQL</productname> provides that may be
+       used for that purpose is <xref linkend="sql-createtransform"/>.
+       The command associates a <productname>PostgreSQL</productname> type and
+       a specific procedural language with a pair of functions to handle the
+       mapping of that type to a corresponding procedural language type
+       and back.
+      </para>
+
+      <para>
+       For a procedural language to support transforms, its call handler is
+       responsible for consulting the <literal>protrftypes</literal> column of
+       a routine's <classname>pg_proc</classname> row to determine which types
+       should have transforms applied.
+       A convenience function <function>get_call_trftypes</function> is
+       available.
+       The call handler must then resolve the <quote>from SQL</quote> function
+       for each affected parameter type, and the <quote>to SQL</quote> function
+       for any affected result.
+       It may use the <function>get_transform_fromsql</function> and
+       <function>get_transform_tosql</function> functions for that.
+      </para>
+
+      <para>
+       The handler must then apply the proper <quote>from SQL</quote> functions
+       to all affected inputs (including elements within array or composite
+       types) and, after calling the target routine, apply the proper
+       <quote>to SQL</quote> functions similarly to any results.
+       If the target routine might interact with the database using SPI,
+       the handler may arrange for the requested transforms to be applied
+       in those operations as well.
+      </para>
+
+      <para>
+       Because the procedural language implementation, and not
+       <productname>PostgreSQL</productname> itself, is responsible for calling
+       the transform functions, it is free to define what it will pass as the
+       parameter to each function (declared as <type>internal</type> for both),
+       and how it will interpret the result (also declared
+       <type>internal</type>) of the <quote>from SQL</quote> function.
+       Effectively, each procedural language's implementation defines the API
+       that must be adhered to by any author of transforms for that language.
+      </para>
+
+      <para>
+       A procedural language might impose limits on where and how it will apply
+       transforms (such as on array or domain types). The
+       <function>get_transform_fromsql</function> and
+       <function>get_transform_tosql</function> functions mentioned above
+       consider each type only shallowly, and will not, for example, return
+       a transform function for a domain type if only its base type was listed
+       in the <literal>TRANSFORM</literal> clause.
+       If a procedural language's call handler does not implement transforms
+       at all, no <literal>TRANSFORM</literal> clause will have any effect
+       for routines declared in that language.
+       The language's validator function can be used to give immediate feedback
+       if a routine is declared with <literal>TRANSFORM</literal> clauses
+       the implementation cannot support.
+      </para>
+
+     </sect3>
+
+    </sect2>
+
+    <sect2 id="plhandler-call-cache">
+     <title>Caching resolved routine information</title>
 
-   <para>
-    Often, the same routine is called many times per SQL statement.
-    A call handler can avoid repeated lookups of information about the
-    called routine by using the
-    <structfield>flinfo-&gt;fn_extra</structfield> field.  This will
-    initially be <symbol>NULL</symbol>, but can be set by the call handler to point at
-    information about the called routine.  On subsequent calls, if
-    <structfield>flinfo-&gt;fn_extra</structfield> is already non-<symbol>NULL</symbol>
-    then it can be used and the information lookup step skipped.  The
-    call handler must make sure that
-    <structfield>flinfo-&gt;fn_extra</structfield> is made to point at
-    memory that will live at least until the end of the current query,
-    since an <structname>FmgrInfo</structname> data structure could be
-    kept that long.  One way to do this is to allocate the extra data
-    in the memory context specified by
-    <structfield>flinfo-&gt;fn_mcxt</structfield>; such data will
-    normally have the same lifespan as the
-    <structname>FmgrInfo</structname> itself.  But the handler could
-    also choose to use a longer-lived memory context so that it can cache
-    routine definition information across queries.
-   </para>
+    <para>
+     Often, the same routine is called many times per SQL statement.
+     A call handler can avoid repeated lookups of information about the
+     called routine by using the
+     <structfield>flinfo-&gt;fn_extra</structfield> field.  This will
+     initially be <symbol>NULL</symbol>, but can be set by the call handler
+     to point at information about the called routine.  On subsequent calls,
+     if <structfield>flinfo-&gt;fn_extra</structfield> is already
+     non-<symbol>NULL</symbol> then it can be used and the information lookup
+     step skipped.  The call handler must make sure that
+     <structfield>flinfo-&gt;fn_extra</structfield> is made to point at
+     memory that will live at least until the end of the current query,
+     since an <structname>FmgrInfo</structname> data structure could be
+     kept that long.  One way to do this is to allocate the extra data
+     in the memory context specified by
+     <structfield>flinfo-&gt;fn_mcxt</structfield>; such data will
+     normally have the same lifespan as the
+     <structname>FmgrInfo</structname> itself.  But the handler could
+     also choose to use a longer-lived memory context so that it can cache
+     routine definition information across queries.
+    </para>
 
-   <para>
-    If the handler supports returning sets, and uses the ValuePerCall mode
-    helper macros described in <xref linkend="xfunc-c-return-set"/>, it must
-    not use <structfield>fn_extra</structfield> during set-returning calls.
-    The helper macros use that field for their own purposes. After
-    <literal>SRF_FIRSTCALL_INIT</literal> has been called, the field will point
-    to a <structname>FuncCallContext</structname> structure, which has
-    a <structfield>user_fctx</structfield> field that can be used similarly,
-    but only through the sequence of calls returning one set result.
-   </para>
+    <para>
+     If the handler supports returning sets, and uses the ValuePerCall mode
+     helper macros described in <xref linkend="xfunc-c-return-set"/>, it must
+     not use <structfield>fn_extra</structfield> during set-returning calls.
+     The helper macros use that field for their own purposes. After
+     <literal>SRF_FIRSTCALL_INIT</literal> has been called, the field will point
+     to a <structname>FuncCallContext</structname> structure, which has
+     a <structfield>user_fctx</structfield> field that can be used similarly,
+     but only through the sequence of calls returning one set result.
+    </para>
 
-   </sect2>
+    </sect2>
 
    </sect1>
 
    <sect1 id="plhandler-validator">
     <title>Validator function</title>
 
-   <para>
-    If a validator is provided by a procedural language, it
-    must be declared as a function taking a single parameter of type
-    <type>oid</type>.  The validator's result is ignored, so it is customarily
-    declared to return <type>void</type>.
-    The validator itself may be written in any procedural language able to
-    receive an <type>oid</type>-typed parameter and query system catalogs.
-   </para>
+    <para>
+     If a validator is provided by a procedural language, it
+     must be declared as a function taking a single parameter of type
+     <type>oid</type>.  The validator's result is ignored, so it is customarily
+     declared to return <type>void</type>.
+     The validator itself may be written in any procedural language able to
+     receive an <type>oid</type>-typed parameter and query system catalogs.
+    </para>
 
-   <para>
-    The validator will be called at
-    the end of a <command>CREATE FUNCTION</command> or
-    <command>CREATE PROCEDURE</command> command that has created
-    or updated a routine written in the procedural language.
-    The passed-in OID is the OID of the routine's <classname>pg_proc</classname>
-    row.  The validator must fetch this row in the usual way, and do
-    whatever checking is appropriate.
-   </para>
+    <para>
+     The validator will be called at
+     the end of a <command>CREATE FUNCTION</command> or
+     <command>CREATE PROCEDURE</command> command that has created
+     or updated a routine written in the procedural language.
+     The passed-in OID is the OID of the routine's
+     <classname>pg_proc</classname> row.  The validator must fetch this row
+     in the usual way, and do whatever checking is appropriate.
+    </para>
 
-   <para>
-    First, call <function>CheckFunctionValidatorAccess()</function> to diagnose
-    explicit calls to the validator that the user could not achieve through
-    <command>CREATE FUNCTION</command> or <command>CREATE PROCEDURE</command>.
-    Typical checks then include verifying
-    that the routine's argument and result types are supported by the
-    language, and that the routine's body is syntactically correct
-    in the language.  If the validator finds the routine to be okay,
-    it should just return.  If it finds an error, it should report that
-    via the normal <function>ereport()</function> error reporting mechanism.
-    Throwing an error will force a transaction rollback and thus prevent
-    the incorrect routine definition from being committed.
-   </para>
+    <para>
+     First, call <function>CheckFunctionValidatorAccess()</function>
+     to diagnose explicit calls to the validator that the user could not achieve
+     through <command>CREATE FUNCTION</command> or
+     <command>CREATE PROCEDURE</command>.
+     Typical checks then include verifying
+     that the routine's argument and result types are supported by the
+     language, and that the routine's body is syntactically correct
+     in the language.  If the validator finds the routine to be okay,
+     it should just return.  If it finds an error, it should report that
+     via the normal <function>ereport()</function> error reporting mechanism.
+     Throwing an error will force a transaction rollback and thus prevent
+     the incorrect routine definition from being committed.
+    </para>
 
-   <para>
-    Validator functions should typically honor the <xref
-    linkend="guc-check-function-bodies"/> parameter: if it is turned off then
-    any expensive or context-sensitive checking should be skipped.  If the
-    language provides for code execution at compilation time, the validator
-    must suppress checks that would induce such execution.  In particular,
-    this parameter is turned off by <application>pg_dump</application> so that it can
-    load procedural language routines without worrying about side effects or
-    dependencies of the routine bodies on other database objects.
-    (Because of this requirement, the call handler should avoid
-    assuming that the validator has fully checked the routine.  The point
-    of having a validator is not to let the call handler omit checks, but
-    to notify the user immediately if there are obvious errors in a
-    <command>CREATE FUNCTION</command> or <command>CREATE PROCEDURE</command>
-    command.)
-   </para>
+    <para>
+     Validator functions should typically honor the <xref
+     linkend="guc-check-function-bodies"/> parameter: if it is turned off then
+     any expensive or context-sensitive checking should be skipped.  If the
+     language provides for code execution at compilation time, the validator
+     must suppress checks that would induce such execution.  In particular,
+     this parameter is turned off by <application>pg_dump</application> so that
+     it can load procedural language routines without worrying about
+     side effects or dependencies of the routine bodies on other database
+     objects.
+     (Because of this requirement, the call handler should avoid
+     assuming that the validator has fully checked the routine.  The point
+     of having a validator is not to let the call handler omit checks, but
+     to notify the user immediately if there are obvious errors in a
+     <command>CREATE FUNCTION</command> or <command>CREATE PROCEDURE</command>
+     command.)
+    </para>
 
-   <para>
-    While the choice of exactly what to check is mostly left to the
-    discretion of the validator function, note that the core
-    <command>CREATE FUNCTION</command> and <command>CREATE PROCEDURE</command>
-    code only executes <literal>SET</literal> clauses
-    attached to a routine when <varname>check_function_bodies</varname> is on.
-    Therefore, checks whose results might be affected by GUC parameters
-    definitely should be skipped when <varname>check_function_bodies</varname> is
-    off, to avoid false failures when reloading a dump.
-   </para>
+    <para>
+     While the choice of exactly what to check is mostly left to the
+     discretion of the validator function, note that the core
+     <command>CREATE FUNCTION</command> and <command>CREATE PROCEDURE</command>
+     code only executes <literal>SET</literal> clauses
+     attached to a routine when <varname>check_function_bodies</varname> is on.
+     Therefore, checks whose results might be affected by GUC parameters
+     definitely should be skipped when <varname>check_function_bodies</varname>
+     is off, to avoid false failures when reloading a dump.
+    </para>
 
-   <para>
-    If a language's call handler does not apply parameter and return type
-    transforms, then no <literal>TRANSFORM</literal> clause in a routine
-    declaration will have any effect. To provide immediate feedback if a
-    declaration contains such a clause, the validator can report a suitable
-    error whenever the <literal>protrftypes</literal> column of the routine's
-    <classname>pg_proc</classname> row is non-null.
-   </para>
+    <para>
+     If a language's call handler does not apply parameter and return type
+     transforms, then no <literal>TRANSFORM</literal> clause in a routine
+     declaration will have any effect. To provide immediate feedback if a
+     declaration contains such a clause, the validator can report a suitable
+     error whenever the <literal>protrftypes</literal> column of the routine's
+     <classname>pg_proc</classname> row is non-null.
+    </para>
 
    </sect1>
 
    <sect1 id="plhandler-inline">
     <title>Inline handler function</title>
 
-   <para>
-    If this handler is provided by a procedural language, it
-    must be declared as a function taking a single parameter of type
-    <type>internal</type>, which will be a pointer
-    to an <structname>InlineCodeBlock</structname> struct when the handler
-    is called.  The result is ignored, so the return type is customarily
-    declared as <type>void</type>.
-    The inline handler itself may be written in any procedural language that
-    permits declaring an <type>internal</type> parameter with a suitable
-    language binding for accessing it as an
-    <structname>InlineCodeBlock</structname> struct.
-   </para>
+    <para>
+     If this handler is provided by a procedural language, it
+     must be declared as a function taking a single parameter of type
+     <type>internal</type>, which will be a pointer
+     to an <structname>InlineCodeBlock</structname> struct when the handler
+     is called.  The result is ignored, so the return type is customarily
+     declared as <type>void</type>.
+     The inline handler itself may be written in any procedural language that
+     permits declaring an <type>internal</type> parameter with a suitable
+     language binding for accessing it as an
+     <structname>InlineCodeBlock</structname> struct.
+    </para>
 
-   <para>
-    The inline handler
-    will be called when a <command>DO</command> statement is executed specifying
-    the procedural language.  The <structname>InlineCodeBlock</structname>
-    struct contains information
-    about the <command>DO</command> statement's parameters, in particular the
-    text of the anonymous code block to be executed.
-    It also contains the OID of the intended procedural language and whether
-    that procedural language is declared as <literal>TRUSTED</literal>, useful
-    if a single inline handler is supporting more than one procedural language.
-    The inline handler should execute the code block and return.
-   </para>
+    <para>
+     The inline handler
+     will be called when a <command>DO</command> statement is executed
+     specifying the procedural language.
+     The <structname>InlineCodeBlock</structname> struct contains information
+     about the <command>DO</command> statement's parameters, in particular the
+     text of the anonymous code block to be executed.
+     It also contains the OID of the intended procedural language and whether
+     that procedural language is declared as <literal>TRUSTED</literal>, useful
+     if a single inline handler is supporting more than one procedural language.
+     The inline handler should execute the code block and return.
+    </para>
 
    </sect1>
 
    <sect1 id="plhandler-packaging">
     <title>Packaging the language handlers</title>
 
-   <para>
-    It's recommended that you wrap all these function declarations,
-    as well as the <command>CREATE LANGUAGE</command> command itself, into
-    an <firstterm>extension</firstterm> so that a simple <command>CREATE EXTENSION</command>
-    command is sufficient to install the language.  See
-    <xref linkend="extend-extensions"/> for information about writing
-    extensions.
-   </para>
+    <para>
+     It's recommended that you wrap all these function declarations,
+     as well as the <command>CREATE LANGUAGE</command> command itself, into
+     an <firstterm>extension</firstterm> so that a simple
+     <command>CREATE EXTENSION</command> command is sufficient to install
+     the language.  See <xref linkend="extend-extensions"/> for information
+     about writing extensions.
+    </para>
 
    </sect1>
 
    <sect1 id="plhandler-examples">
     <title>Example code</title>
 
-   <para>
-    A template for a procedural-language handler written as a C extension is
-    provided in <literal>src/test/modules/plsample</literal>.  This is a
-    working sample demonstrating one way to create a procedural-language
-    handler, process parameters, and return a value.
-   </para>
+    <para>
+     A template for a procedural-language handler written as a C extension is
+     provided in <literal>src/test/modules/plsample</literal>.  This is a
+     working sample demonstrating one way to create a procedural-language
+     handler, process parameters, and return a value.
+    </para>
 
-   <para>
-    The procedural languages included in the standard distribution
-    are good references when trying to write your own language handler.
-    Look into the <filename>src/pl</filename> subdirectory of the source tree.
-    The <xref linkend="sql-createlanguage"/>
-    reference page also has some useful details.
-   </para>
+    <para>
+     The procedural languages included in the standard distribution
+     are good references when trying to write your own language handler.
+     Look into the <filename>src/pl</filename> subdirectory of the source tree.
+     The <xref linkend="sql-createlanguage"/>
+     reference page also has some useful details.
+    </para>
 
    </sect1>
 
-- 
2.7.3

From 339b85536a26be71b1b5eeaba6ae2f483f8a857b Mon Sep 17 00:00:00 2001
From: Chapman Flack <c...@anastigmatix.net>
Date: Thu, 7 Apr 2022 20:52:18 -0400
Subject: [PATCH v2 4/4] PL/Sample "with" TRANSFORM FOR TYPE

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            | 80 +++++++++++++++++++++++++
 src/test/modules/plsample/sql/plsample.sql      | 18 ++++++
 3 files changed, 129 insertions(+)

diff --git a/src/test/modules/plsample/expected/plsample.out b/src/test/modules/plsample/expected/plsample.out
index 8ad5f7a..bab1cb3 100644
--- a/src/test/modules/plsample/expected/plsample.out
+++ b/src/test/modules/plsample/expected/plsample.out
@@ -34,6 +34,37 @@ 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)
+
 CREATE FUNCTION my_trigger_func() RETURNS trigger AS $$
 if TD_event == "INSERT"
     return TD_NEW
diff --git a/src/test/modules/plsample/plsample.c b/src/test/modules/plsample/plsample.c
index 780db72..51fee16 100644
--- a/src/test/modules/plsample/plsample.c
+++ b/src/test/modules/plsample/plsample.c
@@ -23,6 +23,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;
@@ -102,12 +103,15 @@ 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;
 
@@ -123,6 +127,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)
@@ -141,26 +146,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",
@@ -182,6 +253,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 cf652ad..4a31876 100644
--- a/src/test/modules/plsample/sql/plsample.sql
+++ b/src/test/modules/plsample/sql/plsample.sql
@@ -14,6 +14,24 @@ AS $$
 $$ 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}');
+
 CREATE FUNCTION my_trigger_func() RETURNS trigger AS $$
 if TD_event == "INSERT"
     return TD_NEW
-- 
2.7.3

Reply via email to