From 61e6f930d24d72b1212fafaed904205fa9ca15a0 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Thu, 21 Mar 2024 10:20:34 -0400
Subject: [PATCH v2 5/5] docs: Merge all procedural language documentation into
 one chapter.

The documentation index is getting very long, which makes it hard
to find things. These chapters are consecutive and cover closely related
topics, so merge them into one. Hopefully, that will reduce the size
of the index without making it harder for users to find the information
they need.

Rather than actually combining all of the SGML into a single file,
keep one file per <sect1>, and add a glue file that includes all
of them.
---
 doc/src/sgml/filelist.sgml |   1 +
 doc/src/sgml/plang.sgml    |  47 ++++++
 doc/src/sgml/plperl.sgml   |  52 +++----
 doc/src/sgml/plpgsql.sgml  | 300 ++++++++++++++++++-------------------
 doc/src/sgml/plpython.sgml |  80 +++++-----
 doc/src/sgml/pltcl.sgml    |  52 +++----
 doc/src/sgml/postgres.sgml |   8 +-
 doc/src/sgml/xplang.sgml   |  42 +-----
 8 files changed, 292 insertions(+), 290 deletions(-)
 create mode 100644 doc/src/sgml/plang.sgml

diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 1bb662c16f..b801d1cdf3 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -66,6 +66,7 @@
 <!ENTITY xaggr      SYSTEM "xaggr.sgml">
 <!ENTITY xfunc      SYSTEM "xfunc.sgml">
 <!ENTITY xindex     SYSTEM "xindex.sgml">
+<!ENTITY plang      SYSTEM "plang.sgml">
 <!ENTITY xplang     SYSTEM "xplang.sgml">
 <!ENTITY xoper      SYSTEM "xoper.sgml">
 <!ENTITY xtypes     SYSTEM "xtypes.sgml">
diff --git a/doc/src/sgml/plang.sgml b/doc/src/sgml/plang.sgml
new file mode 100644
index 0000000000..cdcf81ca3d
--- /dev/null
+++ b/doc/src/sgml/plang.sgml
@@ -0,0 +1,47 @@
+<!-- doc/src/sgml/xplang.sgml -->
+
+ <chapter id="plang">
+  <title>Procedural Languages</title>
+
+  <indexterm zone="plang">
+   <primary>procedural language</primary>
+  </indexterm>
+
+  <para>
+   <productname>PostgreSQL</productname> allows user-defined functions
+   to be written in other languages besides SQL and C.  These other
+   languages are generically called <firstterm>procedural
+   languages</firstterm> (<acronym>PL</acronym>s).  For a function
+   written in a procedural language, the database server has
+   no built-in knowledge about how to interpret the function's source
+   text. Instead, the task is passed to a special handler that knows
+   the details of the language.  The handler could either do all the
+   work of parsing, syntax analysis, execution, etc. itself, or it
+   could serve as <quote>glue</quote> between
+   <productname>PostgreSQL</productname> and an existing implementation
+   of a programming language.  The handler itself is a
+   C language function compiled into a shared object and
+   loaded on demand, just like any other C function.
+  </para>
+
+  <para>
+   There are currently four procedural languages available in the
+   standard <productname>PostgreSQL</productname> distribution:
+   <application>PL/pgSQL</application> (<xref linkend="plpgsql"/>),
+   <application>PL/Tcl</application> (<xref linkend="pltcl"/>),
+   <application>PL/Perl</application> (<xref linkend="plperl"/>), and
+   <application>PL/Python</application> (<xref linkend="plpython"/>).
+   There are additional procedural languages available that are not
+   included in the core distribution. <xref linkend="external-projects"/>
+   has information about finding them. In addition other languages can
+   be defined by users; the basics of developing a new procedural
+   language are covered in <xref linkend="plhandler"/>.
+  </para>
+
+  &xplang;
+  &plsql;
+  &pltcl;
+  &plperl;
+  &plpython;
+
+</chapter>
diff --git a/doc/src/sgml/plperl.sgml b/doc/src/sgml/plperl.sgml
index 25b1077ad7..912677724a 100644
--- a/doc/src/sgml/plperl.sgml
+++ b/doc/src/sgml/plperl.sgml
@@ -1,6 +1,6 @@
 <!-- doc/src/sgml/plperl.sgml -->
 
- <chapter id="plperl">
+ <sect1 id="plperl">
   <title>PL/Perl &mdash; Perl Procedural Language</title>
 
   <indexterm zone="plperl">
@@ -46,7 +46,7 @@
    </para>
   </note>
 
- <sect1 id="plperl-funcs">
+ <sect2 id="plperl-funcs">
   <title>PL/Perl Functions and Arguments</title>
 
   <para>
@@ -405,9 +405,9 @@ use strict;
   The <literal>feature</literal> pragma is also available to <function>use</function> if your Perl is version 5.10.0 or higher.
   </para>
 
- </sect1>
+ </sect2>
 
- <sect1 id="plperl-data">
+ <sect2 id="plperl-data">
   <title>Data Values in PL/Perl</title>
 
   <para>
@@ -425,12 +425,12 @@ use strict;
    for <type>bool</type> values.  Several examples of transform modules
    are included in the <productname>PostgreSQL</productname> distribution.
   </para>
- </sect1>
+ </sect2>
 
- <sect1 id="plperl-builtins">
+ <sect2 id="plperl-builtins">
   <title>Built-in Functions</title>
 
- <sect2 id="plperl-database">
+ <sect3 id="plperl-database">
   <title>Database Access from PL/Perl</title>
 
   <para>
@@ -779,9 +779,9 @@ CALL transaction_test1();
      </listitem>
     </varlistentry>
    </variablelist>
- </sect2>
+ </sect3>
 
- <sect2 id="plperl-utility-functions">
+ <sect3 id="plperl-utility-functions">
   <title>Utility Functions in PL/Perl</title>
 
    <variablelist>
@@ -993,10 +993,10 @@ CALL transaction_test1();
     </varlistentry>
 
    </variablelist>
-  </sect2>
- </sect1>
+  </sect3>
+ </sect2>
 
- <sect1 id="plperl-global">
+ <sect2 id="plperl-global">
   <title>Global Values in PL/Perl</title>
 
   <para>
@@ -1069,9 +1069,9 @@ $$ LANGUAGE plperl;
    them <literal>SECURITY DEFINER</literal>.  You must of course take care that
    such functions can't be used to do anything unintended.
   </para>
- </sect1>
+ </sect2>
 
- <sect1 id="plperl-trusted">
+ <sect2 id="plperl-trusted">
   <title>Trusted and Untrusted PL/Perl</title>
 
   <indexterm zone="plperl-trusted">
@@ -1169,9 +1169,9 @@ $$ LANGUAGE plperl;
    </para>
   </note>
 
- </sect1>
+ </sect2>
 
- <sect1 id="plperl-triggers">
+ <sect2 id="plperl-triggers">
   <title>PL/Perl Triggers</title>
 
   <para>
@@ -1357,9 +1357,9 @@ CREATE TRIGGER test_valid_id_trig
     FOR EACH ROW EXECUTE FUNCTION valid_id();
 </programlisting>
   </para>
- </sect1>
+ </sect2>
 
- <sect1 id="plperl-event-triggers">
+ <sect2 id="plperl-event-triggers">
   <title>PL/Perl Event Triggers</title>
 
   <para>
@@ -1407,12 +1407,12 @@ CREATE EVENT TRIGGER perl_a_snitch
     EXECUTE FUNCTION perlsnitch();
 </programlisting>
   </para>
- </sect1>
+ </sect2>
 
- <sect1 id="plperl-under-the-hood">
+ <sect2 id="plperl-under-the-hood">
   <title>PL/Perl Under the Hood</title>
 
- <sect2 id="plperl-config">
+ <sect3 id="plperl-config">
   <title>Configuration</title>
 
   <para>
@@ -1538,9 +1538,9 @@ DO 'elog(WARNING, join ", ", sort keys %INC)' LANGUAGE plperl;
      </varlistentry>
 
   </variablelist>
-</sect2>
+</sect3>
 
- <sect2 id="plperl-missing">
+ <sect3 id="plperl-missing">
   <title>Limitations and Missing Features</title>
 
   <para>
@@ -1588,8 +1588,8 @@ DO 'elog(WARNING, join ", ", sort keys %INC)' LANGUAGE plperl;
      </listitem>
    </itemizedlist>
   </para>
- </sect2>
+ </sect3>
 
- </sect1>
+ </sect2>
 
-</chapter>
+</sect1>
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index 6f880b705f..4175e4763b 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -1,13 +1,13 @@
 <!-- doc/src/sgml/plpgsql.sgml -->
 
-<chapter id="plpgsql">
+<sect1 id="plpgsql">
   <title><application>PL/pgSQL</application> &mdash; <acronym>SQL</acronym> Procedural Language</title>
 
  <indexterm zone="plpgsql">
   <primary>PL/pgSQL</primary>
  </indexterm>
 
- <sect1 id="plpgsql-overview">
+ <sect2 id="plpgsql-overview">
   <title>Overview</title>
 
  <para>
@@ -65,7 +65,7 @@
     administrators could choose to remove it.
    </para>
 
-  <sect2 id="plpgsql-advantages">
+  <sect3 id="plpgsql-advantages">
    <title>Advantages of Using <application>PL/pgSQL</application></title>
 
     <para>
@@ -112,9 +112,9 @@
      Also, with <application>PL/pgSQL</application> you can use all
      the data types, operators and functions of SQL.
     </para>
-  </sect2>
+  </sect3>
 
-  <sect2 id="plpgsql-args-results">
+  <sect3 id="plpgsql-args-results">
    <title>Supported Argument and Result Data Types</title>
 
     <para>
@@ -174,10 +174,10 @@
      <xref linkend="plpgsql-declaration-parameters"/> and
      <xref linkend="plpgsql-statements-returning"/>.
     </para>
-  </sect2>
- </sect1>
+  </sect3>
+ </sect2>
 
- <sect1 id="plpgsql-structure">
+ <sect2 id="plpgsql-structure">
   <title>Structure of <application>PL/pgSQL</application></title>
 
   <para>
@@ -310,9 +310,9 @@ $$ LANGUAGE plpgsql;
      outer transaction.  For more about that see <xref
      linkend="plpgsql-error-trapping"/>.
     </para>
-  </sect1>
+  </sect2>
 
-  <sect1 id="plpgsql-declarations">
+  <sect2 id="plpgsql-declarations">
     <title>Declarations</title>
 
     <para>
@@ -393,7 +393,7 @@ DECLARE
 </programlisting>
      </para>
 
-    <sect2 id="plpgsql-declaration-parameters">
+    <sect3 id="plpgsql-declaration-parameters">
      <title>Declaring Function Parameters</title>
 
      <para>
@@ -637,9 +637,9 @@ SELECT add_three_values(1, 2, 4.7);
       The function using <type>anyelement</type> would require you to
       cast the three inputs to the same type manually.
      </para>
-    </sect2>
+    </sect3>
 
-  <sect2 id="plpgsql-declaration-alias">
+  <sect3 id="plpgsql-declaration-alias">
    <title><literal>ALIAS</literal></title>
 
 <synopsis>
@@ -669,9 +669,9 @@ DECLARE
     object, unrestricted use can be confusing.  It's best to use it only
     for the purpose of overriding predetermined names.
    </para>
-   </sect2>
+   </sect3>
 
-  <sect2 id="plpgsql-declaration-type">
+  <sect3 id="plpgsql-declaration-type">
    <title>Copying Types</title>
 
 <synopsis>
@@ -724,9 +724,9 @@ user_ids users.user_id%TYPE ARRAY[4];  -- equivalent to the above
     arguments or result placeholders.
    </para>
 
-  </sect2>
+  </sect3>
 
-    <sect2 id="plpgsql-declaration-rowtypes">
+    <sect3 id="plpgsql-declaration-rowtypes">
      <title>Row Types</title>
 
 <synopsis>
@@ -787,9 +787,9 @@ $$ LANGUAGE plpgsql;
 SELECT merge_fields(t.*) FROM table1 t WHERE ... ;
 </programlisting>
    </para>
-  </sect2>
+  </sect3>
 
-  <sect2 id="plpgsql-declaration-records">
+  <sect3 id="plpgsql-declaration-records">
    <title>Record Types</title>
 
 <synopsis>
@@ -817,9 +817,9 @@ SELECT merge_fields(t.*) FROM table1 t WHERE ... ;
     calling query is parsed, whereas a record variable can change its row
     structure on-the-fly.
    </para>
-  </sect2>
+  </sect3>
 
-  <sect2 id="plpgsql-declaration-collation">
+  <sect3 id="plpgsql-declaration-collation">
    <title>Collation of <application>PL/pgSQL</application> Variables</title>
 
    <indexterm>
@@ -910,10 +910,10 @@ $$ LANGUAGE plpgsql;
     parameters, or local variables used in the expression, just as would
     happen in a plain SQL command.
    </para>
+  </sect3>
   </sect2>
-  </sect1>
 
-  <sect1 id="plpgsql-expressions">
+  <sect2 id="plpgsql-expressions">
   <title>Expressions</title>
 
     <para>
@@ -972,9 +972,9 @@ IF count(*) &gt; 0 FROM my_table THEN ...
      more than one row.  (If it produces no rows, the result is taken as
      NULL.)
     </para>
-  </sect1>
+  </sect2>
 
-  <sect1 id="plpgsql-statements">
+  <sect2 id="plpgsql-statements">
   <title>Basic Statements</title>
 
    <para>
@@ -986,7 +986,7 @@ IF count(*) &gt; 0 FROM my_table THEN ...
     as described in <xref linkend="plpgsql-statements-general-sql"/>.
    </para>
 
-   <sect2 id="plpgsql-statements-assignment">
+   <sect3 id="plpgsql-statements-assignment">
     <title>Assignment</title>
 
     <para>
@@ -1027,9 +1027,9 @@ my_array[1:3] := array[1,2,3];
 complex_array[n].realpart = 12.3;
 </programlisting>
     </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="plpgsql-statements-general-sql">
+   <sect3 id="plpgsql-statements-general-sql">
     <title>Executing SQL Commands</title>
 
     <para>
@@ -1146,9 +1146,9 @@ PERFORM <replaceable>query</replaceable>;
 PERFORM create_mv('cs_session_page_requests_mv', my_query);
 </programlisting>
     </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="plpgsql-statements-sql-onerow">
+   <sect3 id="plpgsql-statements-sql-onerow">
     <title>Executing a Command with a Single-Row Result</title>
 
     <indexterm zone="plpgsql-statements-sql-onerow">
@@ -1307,9 +1307,9 @@ CONTEXT:  PL/pgSQL function get_userid(text) line 6 at SQL statement
      </para>
     </note>
 
-   </sect2>
+   </sect3>
 
-   <sect2 id="plpgsql-statements-executing-dyn">
+   <sect3 id="plpgsql-statements-executing-dyn">
     <title>Executing Dynamic Commands</title>
 
     <para>
@@ -1608,9 +1608,9 @@ EXECUTE format('UPDATE tbl SET %I = $1 WHERE key = $2', colname)
      linkend="plpgsql-porting-ex2"/>, which builds and executes a
      <command>CREATE FUNCTION</command> command to define a new function.
     </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="plpgsql-statements-diagnostics">
+   <sect3 id="plpgsql-statements-diagnostics">
     <title>Obtaining the Result Status</title>
 
     <para>
@@ -1749,9 +1749,9 @@ GET DIAGNOSTICS integer_var = ROW_COUNT;
      affect only the current function.
     </para>
 
-   </sect2>
+   </sect3>
 
-   <sect2 id="plpgsql-statements-null">
+   <sect3 id="plpgsql-statements-null">
     <title>Doing Nothing At All</title>
 
     <para>
@@ -1795,10 +1795,10 @@ END;
      </para>
     </note>
 
-   </sect2>
-  </sect1>
+   </sect3>
+  </sect2>
 
-  <sect1 id="plpgsql-control-structures">
+  <sect2 id="plpgsql-control-structures">
    <title>Control Structures</title>
 
    <para>
@@ -1809,7 +1809,7 @@ END;
     flexible and powerful way.
    </para>
 
-   <sect2 id="plpgsql-statements-returning">
+   <sect3 id="plpgsql-statements-returning">
     <title>Returning from a Function</title>
 
     <para>
@@ -1818,7 +1818,7 @@ END;
      NEXT</command>.
     </para>
 
-    <sect3 id="plpgsql-statements-returning-return">
+    <sect4 id="plpgsql-statements-returning-return">
      <title><command>RETURN</command></title>
 
 <synopsis>
@@ -1877,9 +1877,9 @@ RETURN composite_type_var;
 RETURN (1, 2, 'three'::text);  -- must cast columns to correct types
 </programlisting>
      </para>
-    </sect3>
+    </sect4>
 
-    <sect3 id="plpgsql-statements-returning-return-next">
+    <sect4 id="plpgsql-statements-returning-return-next">
      <title><command>RETURN NEXT</command> and <command>RETURN QUERY</command></title>
     <indexterm>
      <primary>RETURN NEXT</primary>
@@ -2026,10 +2026,10 @@ SELECT * FROM get_available_flightid(CURRENT_DATE);
        increasing this parameter.
       </para>
      </note>
-    </sect3>
-   </sect2>
+    </sect4>
+   </sect3>
 
-   <sect2 id="plpgsql-statements-returning-procedure">
+   <sect3 id="plpgsql-statements-returning-procedure">
     <title>Returning from a Procedure</title>
 
     <para>
@@ -2043,9 +2043,9 @@ SELECT * FROM get_available_flightid(CURRENT_DATE);
      If the procedure has output parameters, the final values of the output
      parameter variables will be returned to the caller.
     </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="plpgsql-statements-calling-procedure">
+   <sect3 id="plpgsql-statements-calling-procedure">
     <title>Calling a Procedure</title>
 
     <para>
@@ -2079,9 +2079,9 @@ $$;
      variable or a field of a composite-type variable.  Currently,
      it cannot be an element of an array.
     </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="plpgsql-conditionals">
+   <sect3 id="plpgsql-conditionals">
     <title>Conditionals</title>
 
     <para>
@@ -2111,7 +2111,7 @@ $$;
     </itemizedlist>
     </para>
 
-    <sect3 id="plpgsql-conditionals-if-then">
+    <sect4 id="plpgsql-conditionals-if-then">
      <title><literal>IF-THEN</literal></title>
 
 <synopsis>
@@ -2136,9 +2136,9 @@ IF v_user_id &lt;&gt; 0 THEN
 END IF;
 </programlisting>
        </para>
-     </sect3>
+     </sect4>
 
-     <sect3 id="plpgsql-conditionals-if-then-else">
+     <sect4 id="plpgsql-conditionals-if-then-else">
       <title><literal>IF-THEN-ELSE</literal></title>
 
 <synopsis>
@@ -2177,9 +2177,9 @@ ELSE
 END IF;
 </programlisting>
      </para>
-    </sect3>
+    </sect4>
 
-     <sect3 id="plpgsql-conditionals-if-then-elsif">
+     <sect4 id="plpgsql-conditionals-if-then-elsif">
       <title><literal>IF-THEN-ELSIF</literal></title>
 
 <synopsis>
@@ -2253,9 +2253,9 @@ END IF;
         for each <literal>IF</literal>, so it is much more cumbersome than
         using <literal>ELSIF</literal> when there are many alternatives.
        </para>
-     </sect3>
+     </sect4>
 
-     <sect3 id="plpgsql-conditionals-simple-case">
+     <sect4 id="plpgsql-conditionals-simple-case">
       <title>Simple <literal>CASE</literal></title>
 
 <synopsis>
@@ -2296,9 +2296,9 @@ CASE x
 END CASE;
 </programlisting>
       </para>
-     </sect3>
+     </sect4>
 
-     <sect3 id="plpgsql-conditionals-searched-case">
+     <sect4 id="plpgsql-conditionals-searched-case">
       <title>Searched <literal>CASE</literal></title>
 
 <synopsis>
@@ -2347,10 +2347,10 @@ END CASE;
        than doing nothing.
       </para>
 
-     </sect3>
-   </sect2>
+     </sect4>
+   </sect3>
 
-   <sect2 id="plpgsql-control-structures-loops">
+   <sect3 id="plpgsql-control-structures-loops">
     <title>Simple Loops</title>
 
     <indexterm zone="plpgsql-control-structures-loops">
@@ -2365,7 +2365,7 @@ END CASE;
      <application>PL/pgSQL</application> function to repeat a series of commands.
     </para>
 
-    <sect3 id="plpgsql-control-structures-loops-loop">
+    <sect4 id="plpgsql-control-structures-loops-loop">
      <title><literal>LOOP</literal></title>
 
 <synopsis>
@@ -2383,9 +2383,9 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>;
       and <literal>CONTINUE</literal> statements within nested loops to
       specify which loop those statements refer to.
      </para>
-    </sect3>
+    </sect4>
 
-     <sect3 id="plpgsql-control-structures-loops-exit">
+     <sect4 id="plpgsql-control-structures-loops-exit">
       <title><literal>EXIT</literal></title>
 
      <indexterm>
@@ -2455,9 +2455,9 @@ BEGIN
 END;
 </programlisting>
        </para>
-     </sect3>
+     </sect4>
 
-     <sect3 id="plpgsql-control-structures-loops-continue">
+     <sect4 id="plpgsql-control-structures-loops-continue">
       <title><literal>CONTINUE</literal></title>
 
      <indexterm>
@@ -2503,10 +2503,10 @@ LOOP
 END LOOP;
 </programlisting>
        </para>
-     </sect3>
+     </sect4>
 
 
-     <sect3 id="plpgsql-control-structures-loops-while">
+     <sect4 id="plpgsql-control-structures-loops-while">
       <title><literal>WHILE</literal></title>
 
      <indexterm>
@@ -2541,9 +2541,9 @@ WHILE NOT done LOOP
 END LOOP;
 </programlisting>
        </para>
-     </sect3>
+     </sect4>
 
-     <sect3 id="plpgsql-integer-for">
+     <sect4 id="plpgsql-integer-for">
       <title><literal>FOR</literal> (Integer Variant)</title>
 
 <synopsis>
@@ -2597,10 +2597,10 @@ END LOOP;
         referenced with a qualified name, using that
         <replaceable>label</replaceable>.
        </para>
-     </sect3>
-   </sect2>
+     </sect4>
+   </sect3>
 
-   <sect2 id="plpgsql-records-iterating">
+   <sect3 id="plpgsql-records-iterating">
     <title>Looping through Query Results</title>
 
     <para>
@@ -2694,9 +2694,9 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>;
      through is to declare it as a cursor.  This is described in
      <xref linkend="plpgsql-cursor-for-loop"/>.
     </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="plpgsql-foreach-array">
+   <sect3 id="plpgsql-foreach-array">
     <title>Looping through Arrays</title>
 
     <para>
@@ -2778,9 +2778,9 @@ NOTICE:  row = {7,8,9}
 NOTICE:  row = {10,11,12}
 </programlisting>
     </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="plpgsql-error-trapping">
+   <sect3 id="plpgsql-error-trapping">
     <title>Trapping Errors</title>
 
     <indexterm>
@@ -2943,7 +2943,7 @@ SELECT merge_db(1, 'dennis');
     </para>
     </example>
 
-   <sect3 id="plpgsql-exception-diagnostics">
+   <sect4 id="plpgsql-exception-diagnostics">
     <title>Obtaining Information about an Error</title>
 
     <para>
@@ -3069,10 +3069,10 @@ EXCEPTION WHEN OTHERS THEN
 END;
 </programlisting>
     </para>
-   </sect3>
-  </sect2>
+   </sect4>
+  </sect3>
 
-  <sect2 id="plpgsql-call-stack">
+  <sect3 id="plpgsql-call-stack">
    <title>Obtaining Execution Location Information</title>
 
    <para>
@@ -3124,10 +3124,10 @@ CONTEXT:  PL/pgSQL function outer_func() line 3 at RETURN
     returns the same sort of stack trace, but describing the location
     at which an error was detected, rather than the current location.
    </para>
+  </sect3>
   </sect2>
-  </sect1>
 
-  <sect1 id="plpgsql-cursors">
+  <sect2 id="plpgsql-cursors">
    <title>Cursors</title>
 
    <indexterm zone="plpgsql-cursors">
@@ -3148,7 +3148,7 @@ CONTEXT:  PL/pgSQL function outer_func() line 3 at RETURN
     large row sets from functions.
    </para>
 
-   <sect2 id="plpgsql-cursor-declarations">
+   <sect3 id="plpgsql-cursor-declarations">
     <title>Declaring Cursor Variables</title>
 
     <para>
@@ -3200,9 +3200,9 @@ DECLARE
      assumes that re-reading the query's output will give consistent
      results, which a volatile function might not do.
     </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="plpgsql-cursor-opening">
+   <sect3 id="plpgsql-cursor-opening">
     <title>Opening Cursors</title>
 
     <para>
@@ -3242,7 +3242,7 @@ DECLARE
      <xref linkend="plpgsql-cursor-returning"/>.
     </para>
 
-    <sect3 id="plpgsql-cursor-opening-open-for-query">
+    <sect4 id="plpgsql-cursor-opening-open-for-query">
      <title><command>OPEN FOR</command> <replaceable>query</replaceable></title>
 
 <synopsis>
@@ -3274,9 +3274,9 @@ OPEN <replaceable>unbound_cursorvar</replaceable> <optional> <optional> NO </opt
 OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey;
 </programlisting>
        </para>
-     </sect3>
+     </sect4>
 
-    <sect3 id="plpgsql-cursor-opening-open-for-execute">
+    <sect4 id="plpgsql-cursor-opening-open-for-execute">
      <title><command>OPEN FOR EXECUTE</command></title>
 
 <synopsis>
@@ -3311,9 +3311,9 @@ OPEN curs1 FOR EXECUTE format('SELECT * FROM %I WHERE col1 = $1',tabname) USING
         is inserted via a <literal>USING</literal> parameter, so it needs
         no quoting.
        </para>
-     </sect3>
+     </sect4>
 
-    <sect3 id="plpgsql-open-bound-cursor">
+    <sect4 id="plpgsql-open-bound-cursor">
      <title>Opening a Bound Cursor</title>
 
 <synopsis>
@@ -3374,10 +3374,10 @@ BEGIN
     OPEN curs4;
 </programlisting>
          </para>
-     </sect3>
-   </sect2>
+     </sect4>
+   </sect3>
 
-   <sect2 id="plpgsql-cursor-using">
+   <sect3 id="plpgsql-cursor-using">
     <title>Using Cursors</title>
 
     <para>
@@ -3401,7 +3401,7 @@ BEGIN
      only until the end of the transaction.
     </para>
 
-    <sect3 id="plpgsql-cursor-using-fetch">
+    <sect4 id="plpgsql-cursor-using-fetch">
      <title><literal>FETCH</literal></title>
 
 <synopsis>
@@ -3456,9 +3456,9 @@ FETCH LAST FROM curs3 INTO x, y;
 FETCH RELATIVE -2 FROM curs4 INTO x;
 </programlisting>
        </para>
-     </sect3>
+     </sect4>
 
-    <sect3 id="plpgsql-cursor-using-move">
+    <sect4 id="plpgsql-cursor-using-move">
      <title><literal>MOVE</literal></title>
 
 <synopsis>
@@ -3483,9 +3483,9 @@ MOVE RELATIVE -2 FROM curs4;
 MOVE FORWARD 2 FROM curs4;
 </programlisting>
        </para>
-     </sect3>
+     </sect4>
 
-    <sect3 id="plpgsql-cursor-using-update-delete">
+    <sect4 id="plpgsql-cursor-using-update-delete">
      <title><literal>UPDATE/DELETE WHERE CURRENT OF</literal></title>
 
 <synopsis>
@@ -3509,9 +3509,9 @@ DELETE FROM <replaceable>table</replaceable> WHERE CURRENT OF <replaceable>curso
 UPDATE foo SET dataval = myval WHERE CURRENT OF curs1;
 </programlisting>
        </para>
-     </sect3>
+     </sect4>
 
-    <sect3 id="plpgsql-cursor-using-close">
+    <sect4 id="plpgsql-cursor-using-close">
      <title><literal>CLOSE</literal></title>
 
 <synopsis>
@@ -3530,9 +3530,9 @@ CLOSE <replaceable>cursor</replaceable>;
 CLOSE curs1;
 </programlisting>
        </para>
-     </sect3>
+     </sect4>
 
-    <sect3 id="plpgsql-cursor-returning">
+    <sect4 id="plpgsql-cursor-returning">
      <title>Returning Cursors</title>
 
        <para>
@@ -3643,10 +3643,10 @@ FETCH ALL FROM b;
 COMMIT;
 </programlisting>
        </para>
-     </sect3>
-   </sect2>
+     </sect4>
+   </sect3>
 
-   <sect2 id="plpgsql-cursor-for-loop">
+   <sect3 id="plpgsql-cursor-for-loop">
     <title>Looping through a Cursor's Result</title>
 
     <para>
@@ -3677,11 +3677,11 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>;
      Each row returned by the cursor is successively assigned to this
      record variable and the loop body is executed.
     </para>
-   </sect2>
+   </sect3>
 
-  </sect1>
+  </sect2>
 
-  <sect1 id="plpgsql-transactions">
+  <sect2 id="plpgsql-transactions">
    <title>Transaction Management</title>
 
    <para>
@@ -3782,12 +3782,12 @@ CALL transaction_test2();
    <para>
     A transaction cannot be ended inside a block with exception handlers.
    </para>
-  </sect1>
+  </sect2>
 
-  <sect1 id="plpgsql-errors-and-messages">
+  <sect2 id="plpgsql-errors-and-messages">
    <title>Errors and Messages</title>
 
-  <sect2 id="plpgsql-statements-raise">
+  <sect3 id="plpgsql-statements-raise">
    <title>Reporting Errors and Messages</title>
 
    <indexterm>
@@ -3984,9 +3984,9 @@ RAISE unique_violation USING MESSAGE = 'Duplicate user ID: ' || user_id;
     </para>
    </note>
 
-  </sect2>
+  </sect3>
 
-  <sect2 id="plpgsql-statements-assert">
+  <sect3 id="plpgsql-statements-assert">
    <title>Checking Assertions</title>
 
    <indexterm>
@@ -4043,11 +4043,11 @@ ASSERT <replaceable class="parameter">condition</replaceable> <optional> , <repl
     the <command>RAISE</command> statement, described above, for that.
    </para>
 
-  </sect2>
+  </sect3>
 
- </sect1>
+ </sect2>
 
- <sect1 id="plpgsql-trigger">
+ <sect2 id="plpgsql-trigger">
   <title>Trigger Functions</title>
 
   <indexterm zone="plpgsql-trigger">
@@ -4066,7 +4066,7 @@ ASSERT <replaceable class="parameter">condition</replaceable> <optional> , <repl
    automatically defined to describe the condition that triggered the call.
   </para>
 
-  <sect2 id="plpgsql-dml-trigger">
+  <sect3 id="plpgsql-dml-trigger">
    <title>Triggers on Data Changes</title>
 
   <para>
@@ -4680,9 +4680,9 @@ CREATE TRIGGER emp_audit_del
 </programlisting>
    </example>
 
-</sect2>
+</sect3>
 
-  <sect2 id="plpgsql-event-trigger">
+  <sect3 id="plpgsql-event-trigger">
    <title>Triggers on Events</title>
 
    <para>
@@ -4742,11 +4742,11 @@ $$ LANGUAGE plpgsql;
 CREATE EVENT TRIGGER snitch ON ddl_command_start EXECUTE FUNCTION snitch();
 </programlisting>
    </example>
-  </sect2>
+  </sect3>
 
-  </sect1>
+  </sect2>
 
-  <sect1 id="plpgsql-implementation">
+  <sect2 id="plpgsql-implementation">
    <title><application>PL/pgSQL</application> under the Hood</title>
 
    <para>
@@ -4754,7 +4754,7 @@ CREATE EVENT TRIGGER snitch ON ddl_command_start EXECUTE FUNCTION snitch();
     frequently important for <application>PL/pgSQL</application> users to know.
    </para>
 
-  <sect2 id="plpgsql-var-subst">
+  <sect3 id="plpgsql-var-subst">
    <title>Variable Substitution</title>
 
    <para>
@@ -4930,9 +4930,9 @@ $$ LANGUAGE plpgsql;
     the utility statement as a string and <command>EXECUTE</command> it.
    </para>
 
-  </sect2>
+  </sect3>
 
-  <sect2 id="plpgsql-plan-caching">
+  <sect3 id="plpgsql-plan-caching">
    <title>Plan Caching</title>
 
    <para>
@@ -5083,11 +5083,11 @@ $$ LANGUAGE plpgsql;
      use of the <literal>now()</literal> function would still be a better idea.
     </para>
 
-  </sect2>
+  </sect3>
 
-  </sect1>
+  </sect2>
 
- <sect1 id="plpgsql-development-tips">
+ <sect2 id="plpgsql-development-tips">
   <title>Tips for Developing in <application>PL/pgSQL</application></title>
 
    <para>
@@ -5124,7 +5124,7 @@ $$ LANGUAGE plpgsql;
     making it easier to recreate and debug functions.
    </para>
 
-  <sect2 id="plpgsql-quote-tips">
+  <sect3 id="plpgsql-quote-tips">
    <title>Handling of Quotation Marks</title>
 
    <para>
@@ -5279,8 +5279,8 @@ a_output := a_output || $$ if v_$$ || referrer_keys.kind || $$ like '$$
    </varlistentry>
   </variablelist>
 
-  </sect2>
-  <sect2 id="plpgsql-extra-checks">
+  </sect3>
+  <sect3 id="plpgsql-extra-checks">
    <title>Additional Compile-Time and Run-Time Checks</title>
 
    <para>
@@ -5400,12 +5400,12 @@ HINT:  Make sure the query returns the exact list of columns.
 (1 row)
 </programlisting>
    </para>
-  </sect2>
- </sect1>
+  </sect3>
+ </sect2>
 
   <!-- **** Porting from Oracle PL/SQL **** -->
 
- <sect1 id="plpgsql-porting">
+ <sect2 id="plpgsql-porting">
   <title>Porting from <productname>Oracle</productname> PL/SQL</title>
 
   <indexterm zone="plpgsql-porting">
@@ -5519,7 +5519,7 @@ HINT:  Make sure the query returns the exact list of columns.
     </itemizedlist>
    </para>
 
-  <sect2 id="plpgsql-porting-examples">
+  <sect3 id="plpgsql-porting-examples">
    <title>Porting Examples</title>
 
    <para>
@@ -5912,9 +5912,9 @@ $$ LANGUAGE plpgsql;
     </calloutlist>
    </para>
    </example>
-  </sect2>
+  </sect3>
 
-  <sect2 id="plpgsql-porting-other">
+  <sect3 id="plpgsql-porting-other">
    <title>Other Things to Watch For</title>
 
    <para>
@@ -5923,7 +5923,7 @@ $$ LANGUAGE plpgsql;
     <productname>PostgreSQL</productname>.
    </para>
 
-   <sect3 id="plpgsql-porting-exceptions">
+   <sect4 id="plpgsql-porting-exceptions">
     <title>Implicit Rollback after Exceptions</title>
 
     <para>
@@ -5953,9 +5953,9 @@ END;
      <command>SAVEPOINT</command> and <command>ROLLBACK TO</command> in a different way
      then some actual thought will be required.
     </para>
-   </sect3>
+   </sect4>
 
-   <sect3 id="plpgsql-porting-other-execute">
+   <sect4 id="plpgsql-porting-other-execute">
     <title><command>EXECUTE</command></title>
 
     <para>
@@ -5968,9 +5968,9 @@ END;
      type <literal>EXECUTE 'SELECT * FROM $1';</literal> will not work
      reliably unless you use these functions.
     </para>
-   </sect3>
+   </sect4>
 
-   <sect3 id="plpgsql-porting-optimization">
+   <sect4 id="plpgsql-porting-optimization">
     <title>Optimizing <application>PL/pgSQL</application> Functions</title>
 
     <para>
@@ -5994,10 +5994,10 @@ CREATE FUNCTION foo(...) RETURNS integer AS $$
 $$ LANGUAGE plpgsql STRICT IMMUTABLE;
 </programlisting>
     </para>
-   </sect3>
-  </sect2>
+   </sect4>
+  </sect3>
 
-  <sect2 id="plpgsql-porting-appendix">
+  <sect3 id="plpgsql-porting-appendix">
    <title>Appendix</title>
 
    <para>
@@ -6126,8 +6126,8 @@ END;
 $$ LANGUAGE plpgsql STRICT IMMUTABLE;
 ]]>
 </programlisting>
-  </sect2>
+  </sect3>
 
- </sect1>
+ </sect2>
 
-</chapter>
+</sect1>
diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgml
index e5d51d6e9f..aaedb30d2e 100644
--- a/doc/src/sgml/plpython.sgml
+++ b/doc/src/sgml/plpython.sgml
@@ -1,6 +1,6 @@
 <!-- doc/src/sgml/plpython.sgml -->
 
-<chapter id="plpython">
+<sect1 id="plpython">
  <title>PL/Python &mdash; Python Procedural Language</title>
 
  <indexterm zone="plpython"><primary>PL/Python</primary></indexterm>
@@ -46,7 +46,7 @@
   </para>
  </note>
 
- <sect1 id="plpython-funcs">
+ <sect2 id="plpython-funcs">
   <title>PL/Python Functions</title>
 
   <para>
@@ -142,9 +142,9 @@ $$ LANGUAGE plpython3u;
    PL/Python.  It is better to treat the function parameters as
    read-only.
   </para>
- </sect1>
+ </sect2>
 
- <sect1 id="plpython-data">
+ <sect2 id="plpython-data">
   <title>Data Values</title>
   <para>
    Generally speaking, the aim of PL/Python is to provide
@@ -153,7 +153,7 @@ $$ LANGUAGE plpython3u;
    below.
   </para>
 
-  <sect2 id="plpython-data-type-mapping">
+  <sect3 id="plpython-data-type-mapping">
    <title>Data Type Mapping</title>
    <para>
     When a PL/Python function is called, its arguments are converted from
@@ -267,9 +267,9 @@ $$ LANGUAGE plpython3u;
     return type and the Python data type of the actual return object
     are not flagged; the value will be converted in any case.
    </para>
-  </sect2>
+  </sect3>
 
-  <sect2 id="plpython-data-null">
+  <sect3 id="plpython-data-null">
    <title>Null, None</title>
   <para>
    If an SQL null value<indexterm><primary>null value</primary><secondary
@@ -299,9 +299,9 @@ $$ LANGUAGE plpython3u;
    function, return the value <symbol>None</symbol>. This can be done whether the
    function is strict or not.
   </para>
-  </sect2>
+  </sect3>
 
-  <sect2 id="plpython-arrays">
+  <sect3 id="plpython-arrays">
    <title>Arrays, Lists</title>
   <para>
    SQL array values are passed into PL/Python as a Python list.  To
@@ -367,9 +367,9 @@ SELECT return_str_arr();
 (1 row)
 </programlisting>
   </para>
-  </sect2>
+  </sect3>
 
-  <sect2 id="plpython-data-composite-types">
+  <sect3 id="plpython-data-composite-types">
    <title>Composite Types</title>
   <para>
    Composite-type arguments are passed to the function as Python mappings. The
@@ -514,9 +514,9 @@ $$ LANGUAGE plpython3u;
 CALL python_triple(5, 10);
 </programlisting>
    </para>
-  </sect2>
+  </sect3>
 
-  <sect2 id="plpython-data-set-returning-funcs">
+  <sect3 id="plpython-data-set-returning-funcs">
    <title>Set-Returning Functions</title>
   <para>
    A <application>PL/Python</application> function can also return sets of
@@ -613,10 +613,10 @@ $$ LANGUAGE plpython3u;
 SELECT * FROM multiout_simple_setof(3);
 </programlisting>
    </para>
-  </sect2>
- </sect1>
+  </sect3>
+ </sect2>
 
- <sect1 id="plpython-sharing">
+ <sect2 id="plpython-sharing">
   <title>Sharing Data</title>
   <para>
    The global dictionary <varname>SD</varname> is available to store
@@ -634,9 +634,9 @@ SELECT * FROM multiout_simple_setof(3);
    <function>myfunc2</function>.  The exception is the data in the
    <varname>GD</varname> dictionary, as mentioned above.
   </para>
- </sect1>
+ </sect2>
 
- <sect1 id="plpython-do">
+ <sect2 id="plpython-do">
   <title>Anonymous Code Blocks</title>
 
   <para>
@@ -652,9 +652,9 @@ $$ LANGUAGE plpython3u;
    An anonymous code block receives no arguments, and whatever value it
    might return is discarded.  Otherwise it behaves just like a function.
   </para>
- </sect1>
+ </sect2>
 
- <sect1 id="plpython-trigger">
+ <sect2 id="plpython-trigger">
   <title>Trigger Functions</title>
 
   <indexterm zone="plpython-trigger">
@@ -767,9 +767,9 @@ $$ LANGUAGE plpython3u;
    <literal>"MODIFY"</literal> to indicate you've modified the new row.
    Otherwise the return value is ignored.
   </para>
- </sect1>
+ </sect2>
 
- <sect1 id="plpython-database">
+ <sect2 id="plpython-database">
   <title>Database Access</title>
 
   <para>
@@ -779,7 +779,7 @@ $$ LANGUAGE plpython3u;
    <literal>plpy.<replaceable>foo</replaceable></literal>.
   </para>
 
-  <sect2 id="plpython-database-access-funcs">
+  <sect3 id="plpython-database-access-funcs">
     <title>Database Access Functions</title>
 
   <para>
@@ -1037,9 +1037,9 @@ $$ LANGUAGE plpython3u;
    </varlistentry>
   </variablelist>
 
-  </sect2>
+  </sect3>
 
-  <sect2 id="plpython-trapping">
+  <sect3 id="plpython-trapping">
    <title>Trapping Errors</title>
 
    <para>
@@ -1109,10 +1109,10 @@ $$ LANGUAGE plpython3u;
     the <quote>SQLSTATE</quote> error code.  This approach provides
     approximately the same functionality
    </para>
-  </sect2>
- </sect1>
+  </sect3>
+ </sect2>
 
- <sect1 id="plpython-subtransaction">
+ <sect2 id="plpython-subtransaction">
   <title>Explicit Subtransactions</title>
 
   <para>
@@ -1124,7 +1124,7 @@ $$ LANGUAGE plpython3u;
    the form of explicit subtransactions.
   </para>
 
-  <sect2 id="plpython-subtransaction-context-managers">
+  <sect3 id="plpython-subtransaction-context-managers">
    <title>Subtransaction Context Managers</title>
 
    <para>
@@ -1189,10 +1189,10 @@ $$ LANGUAGE plpython3u;
     explicit subtransaction block would also cause the subtransaction
     to be rolled back.
    </para>
-  </sect2>
- </sect1>
+  </sect3>
+ </sect2>
 
- <sect1 id="plpython-transactions">
+ <sect2 id="plpython-transactions">
   <title>Transaction Management</title>
 
   <para>
@@ -1229,9 +1229,9 @@ CALL transaction_test1();
   <para>
    Transactions cannot be ended when an explicit subtransaction is active.
   </para>
- </sect1>
+ </sect2>
 
- <sect1 id="plpython-util">
+ <sect2 id="plpython-util">
   <title>Utility Functions</title>
   <para>
    The <literal>plpy</literal> module also provides the functions
@@ -1317,9 +1317,9 @@ plpy.execute("UPDATE tbl SET %s = %s WHERE key = %s" % (
     plpy.quote_literal(keyvalue)))
 </programlisting>
   </para>
- </sect1>
+ </sect2>
 
- <sect1 id="plpython-python23">
+ <sect2 id="plpython-python23">
   <title>Python 2 vs. Python 3</title>
 
   <para>
@@ -1328,9 +1328,9 @@ plpy.execute("UPDATE tbl SET %s = %s WHERE key = %s" % (
    <literal>plpythonu</literal> and <literal>plpython2u</literal> language
    names.
   </para>
- </sect1>
+ </sect2>
 
- <sect1 id="plpython-envar">
+ <sect2 id="plpython-envar">
   <title>Environment Variables</title>
 
   <para>
@@ -1393,5 +1393,5 @@ plpy.execute("UPDATE tbl SET %s = %s WHERE key = %s" % (
    the <command>python</command> man page are only effective in a
    command-line interpreter and not an embedded Python interpreter.)
   </para>
- </sect1>
-</chapter>
+ </sect2>
+</sect1>
diff --git a/doc/src/sgml/pltcl.sgml b/doc/src/sgml/pltcl.sgml
index b31f2c1330..9e12df3816 100644
--- a/doc/src/sgml/pltcl.sgml
+++ b/doc/src/sgml/pltcl.sgml
@@ -1,6 +1,6 @@
 <!-- doc/src/sgml/pltcl.sgml -->
 
- <chapter id="pltcl">
+ <sect1 id="pltcl">
   <title>PL/Tcl &mdash; Tcl Procedural Language</title>
 
   <indexterm zone="pltcl">
@@ -21,7 +21,7 @@
 
   <!-- **** PL/Tcl overview **** -->
 
-  <sect1 id="pltcl-overview">
+  <sect2 id="pltcl-overview">
    <title>Overview</title>
 
    <para>
@@ -70,11 +70,11 @@
     <literal>CREATE EXTENSION pltcl</literal> or
     <literal>CREATE EXTENSION pltclu</literal>.
    </para>
-  </sect1>
+  </sect2>
 
   <!-- **** PL/Tcl description **** -->
 
-   <sect1 id="pltcl-functions">
+   <sect2 id="pltcl-functions">
     <title>PL/Tcl Functions and Arguments</title>
 
     <para>
@@ -238,9 +238,9 @@ $$ LANGUAGE pltcl;
 </programlisting>
     </para>
 
-   </sect1>
+   </sect2>
 
-   <sect1 id="pltcl-data">
+   <sect2 id="pltcl-data">
     <title>Data Values in PL/Tcl</title>
 
     <para>
@@ -252,9 +252,9 @@ $$ LANGUAGE pltcl;
      result type, or for the specified column of a composite result type.
     </para>
 
-   </sect1>
+   </sect2>
 
-   <sect1 id="pltcl-global">
+   <sect2 id="pltcl-global">
     <title>Global Data in PL/Tcl</title>
 
     <indexterm zone="pltcl-global">
@@ -314,9 +314,9 @@ $$ LANGUAGE pltcl;
      An example of using <literal>GD</literal> appears in the
      <function>spi_execp</function> example below.
     </para>
-   </sect1>
+   </sect2>
 
-   <sect1 id="pltcl-dbaccess">
+   <sect2 id="pltcl-dbaccess">
     <title>Database Access from PL/Tcl</title>
 
     <para>
@@ -570,9 +570,9 @@ SELECT 'doesn''t' AS ret
     </variablelist>
     </para>
 
-   </sect1>
+   </sect2>
 
-   <sect1 id="pltcl-trigger">
+   <sect2 id="pltcl-trigger">
     <title>Trigger Functions in PL/Tcl</title>
 
     <indexterm>
@@ -782,9 +782,9 @@ CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
      name; that's supplied from the trigger arguments.  This lets the
      trigger function be reused with different tables.
     </para>
-   </sect1>
+   </sect2>
 
-   <sect1 id="pltcl-event-trigger">
+   <sect2 id="pltcl-event-trigger">
     <title>Event Trigger Functions in PL/Tcl</title>
 
     <indexterm>
@@ -841,9 +841,9 @@ $$ LANGUAGE pltcl;
 CREATE EVENT TRIGGER tcl_a_snitch ON ddl_command_start EXECUTE FUNCTION tclsnitch();
 </programlisting>
     </para>
-   </sect1>
+   </sect2>
 
-   <sect1 id="pltcl-error-handling">
+   <sect2 id="pltcl-error-handling">
     <title>Error Handling in PL/Tcl</title>
 
     <indexterm>
@@ -916,9 +916,9 @@ if {[catch { spi_exec $sql_command }]} {
      (The double colons explicitly specify that <varname>errorCode</varname>
      is a global variable.)
     </para>
-   </sect1>
+   </sect2>
 
-   <sect1 id="pltcl-subtransactions">
+   <sect2 id="pltcl-subtransactions">
     <title>Explicit Subtransactions in PL/Tcl</title>
 
     <indexterm>
@@ -998,9 +998,9 @@ $$ LANGUAGE pltcl;
      contained Tcl code (for instance, due to <function>return</function>) do
      not cause a rollback.
     </para>
-   </sect1>
+   </sect2>
 
-   <sect1 id="pltcl-transactions">
+   <sect2 id="pltcl-transactions">
     <title>Transaction Management</title>
 
     <para>
@@ -1039,9 +1039,9 @@ CALL transaction_test1();
     <para>
      Transactions cannot be ended when an explicit subtransaction is active.
     </para>
-   </sect1>
+   </sect2>
 
-   <sect1 id="pltcl-config">
+   <sect2 id="pltcl-config">
     <title>PL/Tcl Configuration</title>
 
     <para>
@@ -1113,9 +1113,9 @@ CALL transaction_test1();
      </varlistentry>
 
     </variablelist>
-   </sect1>
+   </sect2>
 
-   <sect1 id="pltcl-procnames">
+   <sect2 id="pltcl-procnames">
     <title>Tcl Procedure Names</title>
 
     <para>
@@ -1131,5 +1131,5 @@ CALL transaction_test1();
      when debugging.
     </para>
 
-   </sect1>
- </chapter>
+   </sect2>
+ </sect1>
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 5bc47a9e71..f841570bc8 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -215,13 +215,7 @@ break is not needed in a wider output rendering.
   &trigger;
   &event-trigger;
   &rules;
-
-  &xplang;
-  &plsql;
-  &pltcl;
-  &plperl;
-  &plpython;
-
+  &plang;
   &spi;
   &bgworker;
   &logicaldecoding;
diff --git a/doc/src/sgml/xplang.sgml b/doc/src/sgml/xplang.sgml
index 31d403c480..2c0acdc0f7 100644
--- a/doc/src/sgml/xplang.sgml
+++ b/doc/src/sgml/xplang.sgml
@@ -1,44 +1,6 @@
 <!-- doc/src/sgml/xplang.sgml -->
 
- <chapter id="xplang">
-  <title>Procedural Languages</title>
-
-  <indexterm zone="xplang">
-   <primary>procedural language</primary>
-  </indexterm>
-
-  <para>
-   <productname>PostgreSQL</productname> allows user-defined functions
-   to be written in other languages besides SQL and C.  These other
-   languages are generically called <firstterm>procedural
-   languages</firstterm> (<acronym>PL</acronym>s).  For a function
-   written in a procedural language, the database server has
-   no built-in knowledge about how to interpret the function's source
-   text. Instead, the task is passed to a special handler that knows
-   the details of the language.  The handler could either do all the
-   work of parsing, syntax analysis, execution, etc. itself, or it
-   could serve as <quote>glue</quote> between
-   <productname>PostgreSQL</productname> and an existing implementation
-   of a programming language.  The handler itself is a
-   C language function compiled into a shared object and
-   loaded on demand, just like any other C function.
-  </para>
-
-  <para>
-   There are currently four procedural languages available in the
-   standard <productname>PostgreSQL</productname> distribution:
-   <application>PL/pgSQL</application> (<xref linkend="plpgsql"/>),
-   <application>PL/Tcl</application> (<xref linkend="pltcl"/>),
-   <application>PL/Perl</application> (<xref linkend="plperl"/>), and
-   <application>PL/Python</application> (<xref linkend="plpython"/>).
-   There are additional procedural languages available that are not
-   included in the core distribution. <xref linkend="external-projects"/>
-   has information about finding them. In addition other languages can
-   be defined by users; the basics of developing a new procedural
-   language are covered in <xref linkend="plhandler"/>.
-  </para>
-
-  <sect1 id="xplang-install">
+  <sect1 id="xplang">
    <title>Installing Procedural Languages</title>
 
    <para>
@@ -226,5 +188,3 @@ CREATE TRUSTED LANGUAGE plperl
    </para>
 
   </sect1>
-
-</chapter>
-- 
2.39.3 (Apple Git-145)

