Merge remote-tracking branch 'remotes/PGSQL/REL9_5_STABLE' into XL9_5_STABLE
authorPavan Deolasee <[email protected]>
Thu, 11 Aug 2016 05:07:13 +0000 (10:37 +0530)
committerPavan Deolasee <[email protected]>
Thu, 11 Aug 2016 05:07:13 +0000 (10:37 +0530)
77 files changed:
1  2 
configure
configure.in
contrib/citext/expected/citext_1.out
contrib/citext/sql/citext.sql
doc/bug.template
doc/src/sgml/btree-gist.sgml
doc/src/sgml/catalogs.sgml
doc/src/sgml/ddl.sgml
doc/src/sgml/func.sgml
doc/src/sgml/pgbuffercache.sgml
doc/src/sgml/plpgsql.sgml
doc/src/sgml/ref/create_database.sgml
doc/src/sgml/ref/create_function.sgml
doc/src/sgml/ref/create_index.sgml
doc/src/sgml/ref/create_table.sgml
doc/src/sgml/ref/pgbench.sgml
doc/src/sgml/ref/pgupgrade.sgml
doc/src/sgml/ref/update.sgml
doc/src/sgml/runtime.sgml
src/Makefile.shlib
src/backend/access/heap/heapam.c
src/backend/access/transam/xlogutils.c
src/backend/catalog/dependency.c
src/backend/commands/analyze.c
src/backend/commands/vacuum.c
src/backend/commands/view.c
src/backend/executor/execQual.c
src/backend/executor/execTuples.c
src/backend/nodes/makefuncs.c
src/backend/nodes/nodeFuncs.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/util/plancat.c
src/backend/parser/gram.y
src/backend/parser/parse_utilcmd.c
src/backend/postmaster/autovacuum.c
src/backend/postmaster/pgstat.c
src/backend/postmaster/postmaster.c
src/backend/storage/buffer/bufmgr.c
src/backend/tcop/postgres.c
src/backend/utils/adt/float.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/adt/selfuncs.c
src/backend/utils/misc/postgresql.conf.sample
src/backend/utils/time/snapmgr.c
src/backend/utils/time/tqual.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dumpall.c
src/bin/psql/command.c
src/bin/psql/startup.c
src/bin/psql/tab-complete.c
src/include/access/hash.h
src/include/catalog/pg_proc.h
src/include/executor/executor.h
src/include/executor/tuptable.h
src/include/nodes/primnodes.h
src/include/pg_config.h.win32
src/include/pgstat.h
src/include/utils/builtins.h
src/pl/plpgsql/src/pl_gram.y
src/test/regress/expected/brin.out
src/test/regress/expected/case.out
src/test/regress/expected/case_1.out
src/test/regress/expected/insert_conflict.out
src/test/regress/expected/matview.out
src/test/regress/expected/plpgsql.out
src/test/regress/expected/plpgsql_1.out
src/test/regress/expected/rowsecurity.out
src/test/regress/expected/rowtypes.out
src/test/regress/expected/select_into.out
src/test/regress/sql/case.sql
src/test/regress/sql/insert_conflict.sql
src/test/regress/sql/matview.sql
src/test/regress/sql/plpgsql.sql
src/test/regress/sql/rowsecurity.sql
src/test/regress/sql/rowtypes.sql

diff --cc configure
index d5b5fd4b7cd5c36a8a9cb1e47ef0c5141076eb1c,94c088c62c5173a489a33b049c98a6e7949902d3..3f8dbb37ca8bfaaf649aa3c37b23ee788c328069
+++ b/configure
@@@ -1,8 -1,8 +1,8 @@@
  #! /bin/sh
  # Guess values for system-dependent variables and create Makefiles.
- # Generated by GNU Autoconf 2.69 for PostgreSQL 9.5.3 (Postgres-XL 9.5r1.2).
 -# Generated by GNU Autoconf 2.69 for PostgreSQL 9.5.4.
++# Generated by GNU Autoconf 2.69 for PostgreSQL 9.5.4 (Postgres-XL 9.5r1.2).
  #
 -# Report bugs to <pgsql-bugs@postgresql.org>.
 +# Report bugs to <bugs@postgres-xl.org>.
  #
  #
  # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@@ -582,10 -582,9 +582,10 @@@ MAKEFLAGS
  # Identity of this package.
  PACKAGE_NAME='PostgreSQL'
  PACKAGE_TARNAME='postgresql'
- PACKAGE_VERSION='9.5.3 (Postgres-XL 9.5r1.2)'
 -PACKAGE_VERSION='9.5.4'
 -PACKAGE_STRING='PostgreSQL 9.5.4'
 -PACKAGE_BUGREPORT='[email protected]'
++PACKAGE_VERSION='9.5.4 (Postgres-XL 9.5r1.2)'
 +PACKAGE_XC_VERSION='9.5r1.2'
- PACKAGE_STRING='PostgreSQL 9.5.3 (Postgres-XL 9.5r1.2)'
++PACKAGE_STRING='PostgreSQL 9.5.4 (Postgres-XL 9.5r1.2)'
 +PACKAGE_BUGREPORT='[email protected]'
  PACKAGE_URL=''
  
  ac_unique_file="src/backend/access/common/heaptuple.c"
@@@ -1399,7 -1395,7 +1399,7 @@@ if test "$ac_init_help" = "long"; the
    # Omit some internal or obsolete options to make the list less imposing.
    # This message is too long to be a string in the A/UX 3.1 sh.
    cat <<_ACEOF
- \`configure' configures PostgreSQL 9.5.3 (Postgres-XL 9.5r1.2) to adapt to many kinds of systems.
 -\`configure' configures PostgreSQL 9.5.4 to adapt to many kinds of systems.
++\`configure' configures PostgreSQL 9.5.4 (Postgres-XL 9.5r1.2) to adapt to many kinds of systems.
  
  Usage: $0 [OPTION]... [VAR=VALUE]...
  
  
  if test -n "$ac_init_help"; then
    case $ac_init_help in
-      short | recursive ) echo "Configuration of PostgreSQL 9.5.3 (Postgres-XL 9.5r1.2):";;
 -     short | recursive ) echo "Configuration of PostgreSQL 9.5.4:";;
++     short | recursive ) echo "Configuration of PostgreSQL 9.5.4 (Postgres-XL 9.5r1.2):";;
     esac
    cat <<\_ACEOF
  
  test -n "$ac_init_help" && exit $ac_status
  if $ac_init_version; then
    cat <<\_ACEOF
- PostgreSQL configure 9.5.3 (Postgres-XL 9.5r1.2)
 -PostgreSQL configure 9.5.4
++PostgreSQL configure 9.5.4 (Postgres-XL 9.5r1.2)
  generated by GNU Autoconf 2.69
  
  Copyright (C) 2012 Free Software Foundation, Inc.
@@@ -2325,7 -2321,7 +2325,7 @@@ cat >config.log <<_ACEO
  This file contains any messages produced by compilers while
  running configure, to aid debugging if configure makes a mistake.
  
- It was created by PostgreSQL $as_me 9.5.3 (Postgres-XL 9.5r1.2), which was
 -It was created by PostgreSQL $as_me 9.5.4, which was
++It was created by PostgreSQL $as_me 9.5.4 (Postgres-XL 9.5r1.2), which was
  generated by GNU Autoconf 2.69.  Invocation command line was
  
    $ $0 $@
@@@ -16245,7 -16195,7 +16245,7 @@@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_wr
  # report actual input values of CONFIG_FILES etc. instead of their
  # values after options handling.
  ac_log="
- This file was extended by PostgreSQL $as_me 9.5.3 (Postgres-XL 9.5r1.2), which was
 -This file was extended by PostgreSQL $as_me 9.5.4, which was
++This file was extended by PostgreSQL $as_me 9.5.4 (Postgres-XL 9.5r1.2), which was
  generated by GNU Autoconf 2.69.  Invocation command line was
  
    CONFIG_FILES    = $CONFIG_FILES
@@@ -16315,7 -16265,7 +16315,7 @@@ _ACEO
  cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
  ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
  ac_cs_version="\\
- PostgreSQL config.status 9.5.3 (Postgres-XL 9.5r1.2)
 -PostgreSQL config.status 9.5.4
++PostgreSQL config.status 9.5.4 (Postgres-XL 9.5r1.2)
  configured by $0, generated by GNU Autoconf 2.69,
    with options \\"\$ac_cs_config\\"
  
diff --cc configure.in
index 414a5312a84042c1c1ae682ca0a4fcf5c54de8c7,757aa7f343b1d9414d49035c454cfdc808ad80a0..569cc3f3b8c4a552eb48bc633cf7b7b1bcb9bfaf
@@@ -17,7 -17,7 +17,7 @@@ dnl Read the Autoconf manual for detail
  dnl
  m4_pattern_forbid(^PGAC_)dnl to catch undefined macros
  
- AC_INIT([PostgreSQL], [9.5.3 (Postgres-XL 9.5r1.2)], [[email protected]])
 -AC_INIT([PostgreSQL], [9.5.4], [[email protected]])
++AC_INIT([PostgreSQL], [9.5.4 (Postgres-XL 9.5r1.2)], [[email protected]])
  
  m4_if(m4_defn([m4_PACKAGE_VERSION]), [2.69], [], [m4_fatal([Autoconf version 2.69 is required.
  Untested combinations of 'autoconf' and PostgreSQL versions are not
index d2791a5eb4695d65779cf1eb11e12e2a0b73b960,462d42a3bd822b2b4625ab1d8b1e9f1de09bd6bf..6515fbd571eed16e645377183e2f9d5e53864577
@@@ -519,19 -502,17 +502,17 @@@ SELECT name FROM srt WHERE name !~ 'A$
  (3 rows)
  
  -- SIMILAR TO should be case-insensitive.
 -SELECT name FROM srt WHERE name SIMILAR TO '%a.*';
 +SELECT name FROM srt WHERE name SIMILAR TO '%a.*' order by name;
   name 
  ------
-  AAA
-  aba
- (2 rows)
+  ABA
+ (1 row)
  
 -SELECT name FROM srt WHERE name SIMILAR TO '%A.*';
 +SELECT name FROM srt WHERE name SIMILAR TO '%A.*' order by name;
   name 
  ------
-  AAA
-  aba
- (2 rows)
+  ABA
+ (1 row)
  
  -- Explicit casts.
  SELECT true::citext = 'true' AS t;
Simple merge
index fd6fa6e177234611f65d493700f2f7574666d42c,00a6636321de9142b98018bfa138569ad9981a4c..8476502ce5a402c26fbffeb55b5d5bd87fe2e99b
@@@ -27,7 -27,7 +27,7 @@@ System Configuration
  
    Operating System (example: Linux 2.4.18)    :
  
-   PostgreSQL version (example: PostgreSQL 9.5.3):  Postgres-XL 9.5r1.2
 -  PostgreSQL version (example: PostgreSQL 9.5.4):  PostgreSQL 9.5.4
++  PostgreSQL version (example: PostgreSQL 9.5.4):  Postgres-XL 9.5r1.2
  
    Compiler used (example: gcc 3.3.5)          :
  
Simple merge
Simple merge
index 41bf79e5c28d5bc543b592e158ca21fa74545091,c1fb08bb22b1b73349035b8b838ab5c989c93ff7..98df43338e95f08fe09c78322c8b293319526099
mode 100755,100644..100755
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 136d0a10ba0f3abd12ad1992c3fa70866a181baa,0abe78d37e29c0ff21819d73b08d3917dc214b29..08b1cfffff0a433c2dfbd43bc061e6ad4e2abec6
mode 100755,100644..100755
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 4d512775604a248a44bad7af1017fc422abc29f0,6aab933fbc0c99ba2c1c02b3aa46e9f448e65e28..8e7937cd642662899fb18fc64df2a67b1a36eb26
  #include "nodes/nodeFuncs.h"
  #include "storage/bufmgr.h"
  #include "utils/builtins.h"
- #include "utils/expandeddatum.h"
  #include "utils/lsyscache.h"
  #include "utils/typcache.h"
 -
 +#ifdef XCP
 +#include "pgxc/pgxc.h"
 +#include "utils/memutils.h"
 +#endif
  
  static TupleDesc ExecTypeFromTLInternal(List *targetList,
                                           bool hasoid, bool skipjunk);
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 290afd340d1e00508b380ea7c975238571a7282b,7e622e37fdbce7a72226e4abe42514a4f363194f..43483caf7d61e1868249f22843323abd4c48b36d
  #huge_pages = try                     # on, off, or try
                                        # (change requires restart)
  #temp_buffers = 8MB                   # min 800kB
 -#max_prepared_transactions =                # zero disables the feature
 +#max_prepared_transactions = 10               # zero disables the feature
                                        # (change requires restart)
- # Note:  Increasing max_prepared_transactions costs ~600 bytes of shared memory
- # per transaction slot, plus lock space (see max_locks_per_transaction).
- # It is not advisable to set max_prepared_transactions nonzero unless you
- # actively intend to use prepared transactions.
+ # Caution: it is not advisable to set max_prepared_transactions nonzero unless
+ # you actively intend to use prepared transactions.
  #work_mem = 4MB                               # min 64kB
  #maintenance_work_mem = 64MB          # min 1MB
  #autovacuum_work_mem = -1             # min 1MB, or -1 to use maintenance_work_mem
Simple merge
Simple merge
Simple merge
index b0438def186d1c26133437fc6e3e3632cbe5475d,6ea6be569cdd0b12e4a80f649dd63ada658293d1..e2114552a12e02335eb7622e3466c8e422dbfebf
@@@ -2111,154 -2087,3 +2117,76 @@@ dumpTimestamp(const char *msg
        if (strftime(buf, sizeof(buf), PGDUMP_STRFTIME_FMT, localtime(&now)) != 0)
                fprintf(OPF, "-- %s %s\n\n", msg, buf);
  }
- /*
-  * Append the given string to the buffer, with suitable quoting for passing
-  * the string as a value, in a keyword/pair value in a libpq connection
-  * string
-  */
- static void
- doConnStrQuoting(PQExpBuffer buf, const char *str)
- {
-       const char *s;
-       bool            needquotes;
-       /*
-        * If the string consists entirely of plain ASCII characters, no need to
-        * quote it. This is quite conservative, but better safe than sorry.
-        */
-       needquotes = false;
-       for (s = str; *s; s++)
-       {
-               if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
-                         (*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
-               {
-                       needquotes = true;
-                       break;
-               }
-       }
-       if (needquotes)
-       {
-               appendPQExpBufferChar(buf, '\'');
-               while (*str)
-               {
-                       /* ' and \ must be escaped by to \' and \\ */
-                       if (*str == '\'' || *str == '\\')
-                               appendPQExpBufferChar(buf, '\\');
-                       appendPQExpBufferChar(buf, *str);
-                       str++;
-               }
-               appendPQExpBufferChar(buf, '\'');
-       }
-       else
-               appendPQExpBufferStr(buf, str);
- }
- /*
-  * Append the given string to the shell command being built in the buffer,
-  * with suitable shell-style quoting.
-  */
- static void
- doShellQuoting(PQExpBuffer buf, const char *str)
- {
-       const char *p;
- #ifndef WIN32
-       appendPQExpBufferChar(buf, '\'');
-       for (p = str; *p; p++)
-       {
-               if (*p == '\'')
-                       appendPQExpBufferStr(buf, "'\"'\"'");
-               else
-                       appendPQExpBufferChar(buf, *p);
-       }
-       appendPQExpBufferChar(buf, '\'');
- #else                                                 /* WIN32 */
-       appendPQExpBufferChar(buf, '"');
-       for (p = str; *p; p++)
-       {
-               if (*p == '"')
-                       appendPQExpBufferStr(buf, "\\\"");
-               else
-                       appendPQExpBufferChar(buf, *p);
-       }
-       appendPQExpBufferChar(buf, '"');
- #endif   /* WIN32 */
- }
 +
 +#ifdef PGXC
 +static void
 +dumpNodes(PGconn *conn)
 +{
 +      PQExpBuffer query;
 +      PGresult   *res;
 +      int                     num;
 +      int                     i;
 +
 +      query = createPQExpBuffer();
 +
 +      appendPQExpBuffer(query, "select 'CREATE NODE ' || node_name || '"
 +                                      " WITH (TYPE = ' || chr(39) || (case when node_type='C'"
 +                                      " then 'coordinator' else 'datanode' end) || chr(39)"
 +                                      " || ' , HOST = ' || chr(39) || node_host || chr(39)"
 +                                      " || ', PORT = ' || node_port || (case when nodeis_primary='t'"
 +                                      " then ', PRIMARY' else ' ' end) || (case when nodeis_preferred"
 +                                      " then ', PREFERRED' else ' ' end) || ');' "
 +                                      " as node_query from pg_catalog.pgxc_node order by oid");
 +
 +      res = executeQuery(conn, query->data);
 +
 +      num = PQntuples(res);
 +
 +      if (num > 0)
 +              fprintf(OPF, "--\n-- Nodes\n--\n\n");
 +
 +      for (i = 0; i < num; i++)
 +      {
 +              fprintf(OPF, "%s\n", PQgetvalue(res, i, PQfnumber(res, "node_query")));
 +      }
 +      fprintf(OPF, "\n");
 +
 +      PQclear(res);
 +      destroyPQExpBuffer(query);
 +}
 +
 +static void
 +dumpNodeGroups(PGconn *conn)
 +{
 +      PQExpBuffer query;
 +      PGresult   *res;
 +      int                     num;
 +      int                     i;
 +
 +      query = createPQExpBuffer();
 +
 +      appendPQExpBuffer(query,
 +                                              "select 'CREATE NODE GROUP ' || pgxc_group.group_name"
 +                                              " || ' WITH(' || string_agg(node_name,',') || ');'"
 +                                              " as group_query from pg_catalog.pgxc_node, pg_catalog.pgxc_group"
 +                                              " where pgxc_node.oid = any (pgxc_group.group_members)"
 +                                              " group by pgxc_group.group_name"
 +                                              " order by pgxc_group.group_name");
 +
 +      res = executeQuery(conn, query->data);
 +
 +      num = PQntuples(res);
 +
 +      if (num > 0)
 +              fprintf(OPF, "--\n-- Node groups\n--\n\n");
 +
 +      for (i = 0; i < num; i++)
 +      {
 +              fprintf(OPF, "%s\n", PQgetvalue(res, i, PQfnumber(res, "group_query")));
 +      }
 +      fprintf(OPF, "\n");
 +
 +      PQclear(res);
 +      destroyPQExpBuffer(query);
 +}
 +#endif
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 426b4dc425555578dce08b5d56e160fb56b44803,90a7f2e1cb2db0ba3747c3f5c980032426eaa853..51e75c961a5544178e97e6161c9f3b2d5c7729b9
  #define MEMSET_LOOP_LIMIT 1024
  
  /* Define to the address where bug reports for this package should be sent. */
 -#define PACKAGE_BUGREPORT "p[email protected]"
 +#define PACKAGE_BUGREPORT "p[email protected]"
  
  /* Define to the full name of this package. */
 -#define PACKAGE_NAME "PostgreSQL"
 +#define PACKAGE_NAME "Postgres-XL"
  
  /* Define to the full name and version of this package. */
 -#define PACKAGE_STRING "PostgreSQL 9.5.4"
 +#define PACKAGE_STRING "Postgres-XL 9.5r1.2"
  
  /* Define to the version of this package. */
- #define PACKAGE_VERSION "9.5.3"
+ #define PACKAGE_VERSION "9.5.4"
  
  /* Define to the name of a signed 128-bit integer type. */
  #undef PG_INT128_TYPE
  #define PG_INT64_TYPE long long int
  
  /* PostgreSQL version as a string */
- #define PG_VERSION "9.5.3"
+ #define PG_VERSION "9.5.4"
  
  /* PostgreSQL version as a number */
- #define PG_VERSION_NUM 90503
+ #define PG_VERSION_NUM 90504
  
  /* Define to the one symbol short name of this package. */
 -#define PACKAGE_TARNAME "postgresql"
 +#define PACKAGE_TARNAME "postgres-xl"
 +
 +/* Postgres-XC version as a string */
 +#define PGXC_VERSION "1.1devel"
 +
 +/* Postgres-XC version as a number */
 +#define PGXC_VERSION_NUM 10100
  
  /* Define to the name of the default PostgreSQL service principal in Kerberos.
     (--with-krb-srvnam=NAME) */
Simple merge
Simple merge
Simple merge
Simple merge
index eef7f29196ec4509b4f72f2447ba97807fc8b153,35b6476e501570ee3a273994362eccc30ea8ae97..8ff5abfa05639b1231753168b6c19de4395e0838
@@@ -295,15 -287,54 +295,54 @@@ UPDATE CASE_TB
                  ELSE (3 * j) END
    FROM CASE2_TBL b
    WHERE j = -CASE_TBL.i;
 -SELECT * FROM CASE_TBL;
 +SELECT * FROM CASE_TBL ORDER BY i, f;
    i  |   f   
  -----+-------
 -   8 |  20.2
 -  -9 | -30.3
   -12 |      
 +  -9 | -30.3
    -8 |  10.1
 +   8 |  20.2
  (4 rows)
  
+ --
+ -- Nested CASE expressions
+ --
+ -- This test exercises a bug caused by aliasing econtext->caseValue_isNull
+ -- with the isNull argument of the inner CASE's ExecEvalCase() call.  After
+ -- evaluating the vol(null) expression in the inner CASE's second WHEN-clause,
+ -- the isNull flag for the case test value incorrectly became true, causing
+ -- the third WHEN-clause not to match.  The volatile function calls are needed
+ -- to prevent constant-folding in the planner, which would hide the bug.
+ CREATE FUNCTION vol(text) returns text as
+   'begin return $1; end' language plpgsql volatile;
+ SELECT CASE
+   (CASE vol('bar')
+     WHEN 'foo' THEN 'it was foo!'
+     WHEN vol(null) THEN 'null input'
+     WHEN 'bar' THEN 'it was bar!' END
+   )
+   WHEN 'it was foo!' THEN 'foo recognized'
+   WHEN 'it was bar!' THEN 'bar recognized'
+   ELSE 'unrecognized' END;
+       case      
+ ----------------
+  bar recognized
+ (1 row)
+ -- In this case, we can't inline the SQL function without confusing things.
+ CREATE DOMAIN foodomain AS text;
+ CREATE FUNCTION volfoo(text) returns foodomain as
+   'begin return $1::foodomain; end' language plpgsql volatile;
+ CREATE FUNCTION inline_eq(foodomain, foodomain) returns boolean as
+   'SELECT CASE $2::text WHEN $1::text THEN true ELSE false END' language sql;
+ CREATE OPERATOR = (procedure = inline_eq,
+                    leftarg = foodomain, rightarg = foodomain);
+ SELECT CASE volfoo('bar') WHEN 'foo'::foodomain THEN 'is foo' ELSE 'is not foo' END;
+     case    
+ ------------
+  is not foo
+ (1 row)
  --
  -- Clean up
  --
index 3a798f7b5bc196c4717733edd90a01e1a4b08f54,0000000000000000000000000000000000000000..4fbff97f321b49b858b68a0422d8d76c2f9065bb
mode 100644,000000..100644
--- /dev/null
@@@ -1,317 -1,0 +1,361 @@@
 +--
 +-- CASE
 +-- Test the case statement
 +--
 +CREATE TABLE CASE_TBL (
 +  i integer,
 +  f double precision
 +);
 +CREATE TABLE CASE2_TBL (
 +  i integer,
 +  j integer
 +);
 +INSERT INTO CASE_TBL VALUES (1, 10.1);
 +INSERT INTO CASE_TBL VALUES (2, 20.2);
 +INSERT INTO CASE_TBL VALUES (3, -30.3);
 +INSERT INTO CASE_TBL VALUES (4, NULL);
 +INSERT INTO CASE2_TBL VALUES (1, -1);
 +INSERT INTO CASE2_TBL VALUES (2, -2);
 +INSERT INTO CASE2_TBL VALUES (3, -3);
 +INSERT INTO CASE2_TBL VALUES (2, -4);
 +INSERT INTO CASE2_TBL VALUES (1, NULL);
 +INSERT INTO CASE2_TBL VALUES (NULL, -6);
 +--
 +-- Simplest examples without tables
 +--
 +SELECT '3' AS "One",
 +  CASE
 +    WHEN 1 < 2 THEN 3
 +  END AS "Simple WHEN";
 + One | Simple WHEN 
 +-----+-------------
 + 3   |           3
 +(1 row)
 +
 +SELECT '<NULL>' AS "One",
 +  CASE
 +    WHEN 1 > 2 THEN 3
 +  END AS "Simple default";
 +  One   | Simple default 
 +--------+----------------
 + <NULL> |               
 +(1 row)
 +
 +SELECT '3' AS "One",
 +  CASE
 +    WHEN 1 < 2 THEN 3
 +    ELSE 4
 +  END AS "Simple ELSE";
 + One | Simple ELSE 
 +-----+-------------
 + 3   |           3
 +(1 row)
 +
 +SELECT '4' AS "One",
 +  CASE
 +    WHEN 1 > 2 THEN 3
 +    ELSE 4
 +  END AS "ELSE default";
 + One | ELSE default 
 +-----+--------------
 + 4   |            4
 +(1 row)
 +
 +SELECT '6' AS "One",
 +  CASE
 +    WHEN 1 > 2 THEN 3
 +    WHEN 4 < 5 THEN 6
 +    ELSE 7
 +  END AS "Two WHEN with default";
 + One | Two WHEN with default 
 +-----+-----------------------
 + 6   |                     6
 +(1 row)
 +
 +-- Constant-expression folding shouldn't evaluate unreachable subexpressions
 +SELECT CASE WHEN 1=0 THEN 1/0 WHEN 1=1 THEN 1 ELSE 2/0 END;
 + case 
 +------
 +    1
 +(1 row)
 +
 +SELECT CASE 1 WHEN 0 THEN 1/0 WHEN 1 THEN 1 ELSE 2/0 END;
 + case 
 +------
 +    1
 +(1 row)
 +
 +-- However we do not currently suppress folding of potentially
 +-- reachable subexpressions
 +SELECT CASE WHEN i > 100 THEN 1/0 ELSE 0 END FROM case_tbl;
 +ERROR:  division by zero
 +-- Test for cases involving untyped literals in test expression
 +SELECT CASE 'a' WHEN 'a' THEN 1 ELSE 2 END;
 + case 
 +------
 +    1
 +(1 row)
 +
 +--
 +-- Examples of targets involving tables
 +--
 +SELECT '' AS "Five",
 +  CASE
 +    WHEN i >= 3 THEN i
 +  END AS ">= 3 or Null"
 +  FROM CASE_TBL 
 +  ORDER BY 2;
 + Five | >= 3 or Null 
 +------+--------------
 +      |            3
 +      |            4
 +      |             
 +      |             
 +(4 rows)
 +
 +SELECT '' AS "Five",
 +  CASE WHEN i >= 3 THEN (i + i)
 +       ELSE i
 +  END AS "Simplest Math"
 +  FROM CASE_TBL 
 +  ORDER BY 2;
 + Five | Simplest Math 
 +------+---------------
 +      |             1
 +      |             2
 +      |             6
 +      |             8
 +(4 rows)
 +
 +SELECT '' AS "Five", i AS "Value",
 +  CASE WHEN (i < 0) THEN 'small'
 +       WHEN (i = 0) THEN 'zero'
 +       WHEN (i = 1) THEN 'one'
 +       WHEN (i = 2) THEN 'two'
 +       ELSE 'big'
 +  END AS "Category"
 +  FROM CASE_TBL 
 +  ORDER BY 2, 3;
 + Five | Value | Category 
 +------+-------+----------
 +      |     1 | one
 +      |     2 | two
 +      |     3 | big
 +      |     4 | big
 +(4 rows)
 +
 +SELECT '' AS "Five",
 +  CASE WHEN ((i < 0) or (i < 0)) THEN 'small'
 +       WHEN ((i = 0) or (i = 0)) THEN 'zero'
 +       WHEN ((i = 1) or (i = 1)) THEN 'one'
 +       WHEN ((i = 2) or (i = 2)) THEN 'two'
 +       ELSE 'big'
 +  END AS "Category"
 +  FROM CASE_TBL
 +  ORDER BY 2;
 + Five | Category 
 +------+----------
 +      | big
 +      | big
 +      | one
 +      | two
 +(4 rows)
 +
 +--
 +-- Examples of qualifications involving tables
 +--
 +--
 +-- NULLIF() and COALESCE()
 +-- Shorthand forms for typical CASE constructs
 +--  defined in the SQL standard.
 +--
 +SELECT * FROM CASE_TBL WHERE COALESCE(f,i) = 4;
 + i | f 
 +---+---
 + 4 |  
 +(1 row)
 +
 +SELECT * FROM CASE_TBL WHERE NULLIF(f,i) = 2;
 + i | f 
 +---+---
 +(0 rows)
 +
 +SELECT COALESCE(a.f, b.i, b.j)
 +  FROM CASE_TBL a, CASE2_TBL b 
 +  ORDER BY coalesce;
 + coalesce 
 +----------
 +    -30.3
 +    -30.3
 +    -30.3
 +    -30.3
 +    -30.3
 +    -30.3
 +       -6
 +        1
 +        1
 +        2
 +        2
 +        3
 +     10.1
 +     10.1
 +     10.1
 +     10.1
 +     10.1
 +     10.1
 +     20.2
 +     20.2
 +     20.2
 +     20.2
 +     20.2
 +     20.2
 +(24 rows)
 +
 +SELECT *
 +  FROM CASE_TBL a, CASE2_TBL b
 +  WHERE COALESCE(a.f, b.i, b.j) = 2 
 +  ORDER BY a.i, a.f, b.i, b.j;
 + i | f | i | j  
 +---+---+---+----
 + 4 |   | 2 | -4
 + 4 |   | 2 | -2
 +(2 rows)
 +
 +SELECT '' AS Five, NULLIF(a.i,b.i) AS "NULLIF(a.i,b.i)",
 +  NULLIF(b.i, 4) AS "NULLIF(b.i,4)"
 +  FROM CASE_TBL a, CASE2_TBL b 
 +  ORDER BY 2, 3;
 + five | NULLIF(a.i,b.i) | NULLIF(b.i,4) 
 +------+-----------------+---------------
 +      |               1 |             2
 +      |               1 |             2
 +      |               1 |             3
 +      |               1 |              
 +      |               2 |             1
 +      |               2 |             1
 +      |               2 |             3
 +      |               2 |              
 +      |               3 |             1
 +      |               3 |             1
 +      |               3 |             2
 +      |               3 |             2
 +      |               3 |              
 +      |               4 |             1
 +      |               4 |             1
 +      |               4 |             2
 +      |               4 |             2
 +      |               4 |             3
 +      |               4 |              
 +      |                 |             1
 +      |                 |             1
 +      |                 |             2
 +      |                 |             2
 +      |                 |             3
 +(24 rows)
 +
 +SELECT '' AS "Two", *
 +  FROM CASE_TBL a, CASE2_TBL b
 +  WHERE COALESCE(f,b.i) = 2 
 +  ORDER BY a.i, a.f, b.i, b.j;
 + Two | i | f | i | j  
 +-----+---+---+---+----
 +     | 4 |   | 2 | -4
 +     | 4 |   | 2 | -2
 +(2 rows)
 +
 +--
 +-- Examples of updates involving tables
 +--
 +UPDATE CASE_TBL
 +  SET i = CASE WHEN i >= 3 THEN (- i)
 +                ELSE (2 * i) END;
 +ERROR:  could not plan this distributed update
 +DETAIL:  correlated UPDATE or updating distribution column currently not supported in Postgres-XL.
 +SELECT * FROM CASE_TBL ORDER BY i, f;
 + i |   f   
 +---+-------
 + 1 |  10.1
 + 2 |  20.2
 + 3 | -30.3
 + 4 |      
 +(4 rows)
 +
 +UPDATE CASE_TBL
 +  SET i = CASE WHEN i >= 2 THEN (2 * i)
 +                ELSE (3 * i) END;
 +ERROR:  could not plan this distributed update
 +DETAIL:  correlated UPDATE or updating distribution column currently not supported in Postgres-XL.
 +SELECT * FROM CASE_TBL ORDER BY i, f;
 + i |   f   
 +---+-------
 + 1 |  10.1
 + 2 |  20.2
 + 3 | -30.3
 + 4 |      
 +(4 rows)
 +
 +UPDATE CASE_TBL
 +  SET i = CASE WHEN b.i >= 2 THEN (2 * j)
 +                ELSE (3 * j) END
 +  FROM CASE2_TBL b
 +  WHERE j = -CASE_TBL.i;
 +ERROR:  could not plan this distributed update
 +DETAIL:  correlated UPDATE or updating distribution column currently not supported in Postgres-XL.
 +SELECT * FROM CASE_TBL ORDER BY i, f;
 + i |   f   
 +---+-------
 + 1 |  10.1
 + 2 |  20.2
 + 3 | -30.3
 + 4 |      
 +(4 rows)
 +
++--
++-- Nested CASE expressions
++--
++-- This test exercises a bug caused by aliasing econtext->caseValue_isNull
++-- with the isNull argument of the inner CASE's ExecEvalCase() call.  After
++-- evaluating the vol(null) expression in the inner CASE's second WHEN-clause,
++-- the isNull flag for the case test value incorrectly became true, causing
++-- the third WHEN-clause not to match.  The volatile function calls are needed
++-- to prevent constant-folding in the planner, which would hide the bug.
++CREATE FUNCTION vol(text) returns text as
++  'begin return $1; end' language plpgsql volatile;
++SELECT CASE
++  (CASE vol('bar')
++    WHEN 'foo' THEN 'it was foo!'
++    WHEN vol(null) THEN 'null input'
++    WHEN 'bar' THEN 'it was bar!' END
++  )
++  WHEN 'it was foo!' THEN 'foo recognized'
++  WHEN 'it was bar!' THEN 'bar recognized'
++  ELSE 'unrecognized' END;
++      case      
++----------------
++ bar recognized
++(1 row)
++
++-- In this case, we can't inline the SQL function without confusing things.
++CREATE DOMAIN foodomain AS text;
++CREATE FUNCTION volfoo(text) returns foodomain as
++  'begin return $1::foodomain; end' language plpgsql volatile;
++CREATE FUNCTION inline_eq(foodomain, foodomain) returns boolean as
++  'SELECT CASE $2::text WHEN $1::text THEN true ELSE false END' language sql;
++CREATE OPERATOR = (procedure = inline_eq,
++                   leftarg = foodomain, rightarg = foodomain);
++SELECT CASE volfoo('bar') WHEN 'foo'::foodomain THEN 'is foo' ELSE 'is not foo' END;
++    case    
++------------
++ is not foo
++(1 row)
++
 +--
 +-- Clean up
 +--
 +DROP TABLE CASE_TBL;
 +DROP TABLE CASE2_TBL;
++DROP OPERATOR = (foodomain, foodomain);
++DROP FUNCTION inline_eq(foodomain, foodomain);
++DROP FUNCTION volfoo(text);
++DROP DOMAIN foodomain;
++DROP FUNCTION vol(text);
index 49a9b9fe1eacf4ebe898cc3f93c01a7ba4e44166,113b1be3070083148e945e7590ec850eda1f59ea..4d544a1da3617b3d20e227e9ce6a1aa80d2c6ba1
@@@ -479,6 -454,21 +479,21 @@@ ERROR:  column excluded.oid does not ex
  LINE 1: ...values (1) on conflict (key) do update set data = excluded.o...
                                                               ^
  drop table syscolconflicttest;
 -create table insertconflict (a bigint, b bigint);
+ --
+ -- Previous tests all managed to not test any expressions requiring
+ -- planner preprocessing ...
+ --
++create table insertconflict (a bigint, b bigint) distribute by replication;
+ create unique index insertconflicti1 on insertconflict(coalesce(a, 0));
+ create unique index insertconflicti2 on insertconflict(b)
+   where coalesce(a, 1) > 0;
+ insert into insertconflict values (1, 2)
+ on conflict (coalesce(a, 0)) do nothing;
+ insert into insertconflict values (1, 2)
+ on conflict (b) where coalesce(a, 1) > 0 do nothing;
+ insert into insertconflict values (1, 2)
+ on conflict (b) where coalesce(a, 1) > 1 do nothing;
+ drop table insertconflict;
  -- ******************************************************************
  -- *                                                                *
  -- * Test inheritance (example taken from tutorial)                 *
index 7c655ca8b2a792d79a03678d5d43364a50ed59da,4ab23700299f870bdd73fc7153e091af525ffbc9..0992ff6a215514c112b53015fec9e8ea256be8e7
@@@ -458,28 -453,68 +458,67 @@@ SELECT * FROM boxmv ORDER BY id
  
  DROP TABLE boxes CASCADE;
  NOTICE:  drop cascades to materialized view boxmv
---- make sure that column names are handled correctly
- CREATE TABLE v (i int, j int);
- CREATE MATERIALIZED VIEW mv_v (ii) AS SELECT i, j AS jj FROM v;
- ALTER TABLE v RENAME COLUMN i TO x;
- INSERT INTO v values (1, 2);
- CREATE UNIQUE INDEX mv_v_ii ON mv_v (ii);
- REFRESH MATERIALIZED VIEW mv_v;
- UPDATE v SET j = 3 WHERE x = 1;
- SELECT * FROM v;
+ CREATE TABLE mvtest_v (i int, j int);
+ CREATE MATERIALIZED VIEW mvtest_mv_v (ii, jj, kk) AS SELECT i, j FROM mvtest_v; -- error
+ ERROR:  too many column names were specified
+ CREATE MATERIALIZED VIEW mvtest_mv_v (ii, jj) AS SELECT i, j FROM mvtest_v; -- ok
+ CREATE MATERIALIZED VIEW mvtest_mv_v_2 (ii) AS SELECT i, j FROM mvtest_v; -- ok
+ CREATE MATERIALIZED VIEW mvtest_mv_v_3 (ii, jj, kk) AS SELECT i, j FROM mvtest_v WITH NO DATA; -- error
+ ERROR:  too many column names were specified
+ CREATE MATERIALIZED VIEW mvtest_mv_v_3 (ii, jj) AS SELECT i, j FROM mvtest_v WITH NO DATA; -- ok
+ CREATE MATERIALIZED VIEW mvtest_mv_v_4 (ii) AS SELECT i, j FROM mvtest_v WITH NO DATA; -- ok
+ ALTER TABLE mvtest_v RENAME COLUMN i TO x;
+ INSERT INTO mvtest_v values (1, 2);
+ CREATE UNIQUE INDEX mvtest_mv_v_ii ON mvtest_mv_v (ii);
+ REFRESH MATERIALIZED VIEW mvtest_mv_v;
+ UPDATE mvtest_v SET j = 3 WHERE x = 1;
 -REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv_v;
++-- REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv_v;
+ REFRESH MATERIALIZED VIEW mvtest_mv_v_2;
+ REFRESH MATERIALIZED VIEW mvtest_mv_v_3;
+ REFRESH MATERIALIZED VIEW mvtest_mv_v_4;
+ SELECT * FROM mvtest_v;
   x | j 
  ---+---
   1 | 3
  (1 row)
  
- SELECT * FROM mv_v;
+ SELECT * FROM mvtest_mv_v;
   ii | jj 
  ----+----
 -  1 |  3
 +  1 |  2
  (1 row)
  
- DROP TABLE v CASCADE;
- NOTICE:  drop cascades to materialized view mv_v
+ SELECT * FROM mvtest_mv_v_2;
+  ii | j 
+ ----+---
+   1 | 3
+ (1 row)
+ SELECT * FROM mvtest_mv_v_3;
+  ii | jj 
+ ----+----
+   1 |  3
+ (1 row)
+ SELECT * FROM mvtest_mv_v_4;
+  ii | j 
+ ----+---
+   1 | 3
+ (1 row)
+ DROP TABLE mvtest_v CASCADE;
+ NOTICE:  drop cascades to 4 other objects
+ DETAIL:  drop cascades to materialized view mvtest_mv_v
+ drop cascades to materialized view mvtest_mv_v_2
+ drop cascades to materialized view mvtest_mv_v_3
+ drop cascades to materialized view mvtest_mv_v_4
+ -- make sure that create WITH NO DATA does not plan the query (bug #13907)
+ create materialized view mvtest_error as select 1/0 as x;  -- fail
+ ERROR:  division by zero
+ create materialized view mvtest_error as select 1/0 as x with no data;
+ refresh materialized view mvtest_error;  -- fail here
+ ERROR:  division by zero
+ drop materialized view mvtest_error;
  -- make sure that matview rows can be referenced as source rows (bug #9398)
  CREATE TABLE v AS SELECT generate_series(1,10) AS a;
  CREATE MATERIALIZED VIEW mv_v AS SELECT a FROM v WHERE a <= 5;
Simple merge
index 4f9dea9c2503db5117c5f73279d958074bd3c585,0000000000000000000000000000000000000000..55a288a04893ff6e961154073bccf9de182bdca0
mode 100644,000000..100644
--- /dev/null
@@@ -1,5531 -1,0 +1,5601 @@@
- -- access to call stack
 +--
 +-- PLPGSQL
 +--
 +-- Scenario:
 +--
 +--     A building with a modern TP cable installation where any
 +--     of the wall connectors can be used to plug in phones,
 +--     ethernet interfaces or local office hubs. The backside
 +--     of the wall connectors is wired to one of several patch-
 +--     fields in the building.
 +--
 +--     In the patchfields, there are hubs and all the slots
 +--     representing the wall connectors. In addition there are
 +--     slots that can represent a phone line from the central
 +--     phone system.
 +--
 +--     Triggers ensure consistency of the patching information.
 +--
 +--     Functions are used to build up powerful views that let
 +--     you look behind the wall when looking at a patchfield
 +--     or into a room.
 +--
 +create table Room (
 +    roomno    char(8),
 +    comment   text
 +);
 +create unique index Room_rno on Room using btree (roomno bpchar_ops);
 +create table WSlot (
 +    slotname  char(20),
 +    roomno    char(8),
 +    slotlink  char(20),
 +    backlink  char(20)
 +);
 +create unique index WSlot_name on WSlot using btree (slotname bpchar_ops);
 +create table PField (
 +    name      text,
 +    comment   text
 +) distribute by replication;
 +create unique index PField_name on PField using btree (name text_ops);
 +create table PSlot (
 +    slotname  char(20),
 +    pfname    text,
 +    slotlink  char(20),
 +    backlink  char(20)
 +);
 +create unique index PSlot_name on PSlot using btree (slotname bpchar_ops);
 +create table PLine (
 +    slotname  char(20),
 +    phonenumber       char(20),
 +    comment   text,
 +    backlink  char(20)
 +);
 +create unique index PLine_name on PLine using btree (slotname bpchar_ops);
 +create table Hub (
 +    name      char(14),
 +    comment   text,
 +    nslots    integer
 +);
 +create unique index Hub_name on Hub using btree (name bpchar_ops);
 +create table HSlot (
 +    slotname  char(20),
 +    hubname   char(14),
 +    slotno    integer,
 +    slotlink  char(20)
 +);
 +create unique index HSlot_name on HSlot using btree (slotname bpchar_ops);
 +create index HSlot_hubname on HSlot using btree (hubname bpchar_ops);
 +create table System (
 +    name      text,
 +    comment   text
 +);
 +create unique index System_name on System using btree (name text_ops);
 +create table IFace (
 +    slotname  char(20),
 +    sysname   text,
 +    ifname    text,
 +    slotlink  char(20)
 +);
 +create unique index IFace_name on IFace using btree (slotname bpchar_ops);
 +create table PHone (
 +    slotname  char(20),
 +    comment   text,
 +    slotlink  char(20)
 +);
 +create unique index PHone_name on PHone using btree (slotname bpchar_ops);
 +-- ************************************************************
 +-- *
 +-- * Trigger procedures and functions for the patchfield
 +-- * test of PL/pgSQL
 +-- *
 +-- ************************************************************
 +-- ************************************************************
 +-- * AFTER UPDATE on Room
 +-- *  - If room no changes let wall slots follow
 +-- ************************************************************
 +create function tg_room_au() returns trigger as '
 +begin
 +    if new.roomno != old.roomno then
 +        update WSlot set roomno = new.roomno where roomno = old.roomno;
 +    end if;
 +    return new;
 +end;
 +' language plpgsql;
 +create trigger tg_room_au after update
 +    on Room for each row execute procedure tg_room_au();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * AFTER DELETE on Room
 +-- *  - delete wall slots in this room
 +-- ************************************************************
 +create function tg_room_ad() returns trigger as '
 +begin
 +    delete from WSlot where roomno = old.roomno;
 +    return old;
 +end;
 +' language plpgsql;
 +create trigger tg_room_ad after delete
 +    on Room for each row execute procedure tg_room_ad();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * BEFORE INSERT or UPDATE on WSlot
 +-- *  - Check that room exists
 +-- ************************************************************
 +create function tg_wslot_biu() returns trigger as $$
 +begin
 +    if count(*) = 0 from Room where roomno = new.roomno then
 +        raise exception 'Room % does not exist', new.roomno;
 +    end if;
 +    return new;
 +end;
 +$$ language plpgsql;
 +create trigger tg_wslot_biu before insert or update
 +    on WSlot for each row execute procedure tg_wslot_biu();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * AFTER UPDATE on PField
 +-- *  - Let PSlots of this field follow
 +-- ************************************************************
 +create function tg_pfield_au() returns trigger as '
 +begin
 +    if new.name != old.name then
 +        update PSlot set pfname = new.name where pfname = old.name;
 +    end if;
 +    return new;
 +end;
 +' language plpgsql;
 +create trigger tg_pfield_au after update
 +    on PField for each row execute procedure tg_pfield_au();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * AFTER DELETE on PField
 +-- *  - Remove all slots of this patchfield
 +-- ************************************************************
 +create function tg_pfield_ad() returns trigger as '
 +begin
 +    delete from PSlot where pfname = old.name;
 +    return old;
 +end;
 +' language plpgsql;
 +create trigger tg_pfield_ad after delete
 +    on PField for each row execute procedure tg_pfield_ad();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * BEFORE INSERT or UPDATE on PSlot
 +-- *  - Ensure that our patchfield does exist
 +-- ************************************************************
 +create function tg_pslot_biu() returns trigger as $proc$
 +declare
 +    pfrec     record;
 +    ps          alias for new;
 +begin
 +    select into pfrec * from PField where name = ps.pfname;
 +    if not found then
 +        raise exception $$Patchfield "%" does not exist$$, ps.pfname;
 +    end if;
 +    return ps;
 +end;
 +$proc$ language plpgsql;
 +create trigger tg_pslot_biu before insert or update
 +    on PSlot for each row execute procedure tg_pslot_biu();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * AFTER UPDATE on System
 +-- *  - If system name changes let interfaces follow
 +-- ************************************************************
 +create function tg_system_au() returns trigger as '
 +begin
 +    if new.name != old.name then
 +        update IFace set sysname = new.name where sysname = old.name;
 +    end if;
 +    return new;
 +end;
 +' language plpgsql;
 +create trigger tg_system_au after update
 +    on System for each row execute procedure tg_system_au();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * BEFORE INSERT or UPDATE on IFace
 +-- *  - set the slotname to IF.sysname.ifname
 +-- ************************************************************
 +create function tg_iface_biu() returns trigger as $$
 +declare
 +    sname     text;
 +    sysrec    record;
 +begin
 +    select into sysrec * from system where name = new.sysname;
 +    if not found then
 +        raise exception $q$system "%" does not exist$q$, new.sysname;
 +    end if;
 +    sname := 'IF.' || new.sysname;
 +    sname := sname || '.';
 +    sname := sname || new.ifname;
 +    if length(sname) > 20 then
 +        raise exception 'IFace slotname "%" too long (20 char max)', sname;
 +    end if;
 +    new.slotname := sname;
 +    return new;
 +end;
 +$$ language plpgsql;
 +create trigger tg_iface_biu before insert or update
 +    on IFace for each row execute procedure tg_iface_biu();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * AFTER INSERT or UPDATE or DELETE on Hub
 +-- *  - insert/delete/rename slots as required
 +-- ************************************************************
 +create function tg_hub_a() returns trigger as '
 +declare
 +    hname     text;
 +    dummy     integer;
 +begin
 +    if tg_op = ''INSERT'' then
 +      dummy := tg_hub_adjustslots(new.name, 0, new.nslots);
 +      return new;
 +    end if;
 +    if tg_op = ''UPDATE'' then
 +      if new.name != old.name then
 +          update HSlot set hubname = new.name where hubname = old.name;
 +      end if;
 +      dummy := tg_hub_adjustslots(new.name, old.nslots, new.nslots);
 +      return new;
 +    end if;
 +    if tg_op = ''DELETE'' then
 +      dummy := tg_hub_adjustslots(old.name, old.nslots, 0);
 +      return old;
 +    end if;
 +end;
 +' language plpgsql;
 +create trigger tg_hub_a after insert or update or delete
 +    on Hub for each row execute procedure tg_hub_a();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * Support function to add/remove slots of Hub
 +-- ************************************************************
 +create function tg_hub_adjustslots(hname bpchar,
 +                                   oldnslots integer,
 +                                   newnslots integer)
 +returns integer as '
 +begin
 +    if newnslots = oldnslots then
 +        return 0;
 +    end if;
 +    if newnslots < oldnslots then
 +        delete from HSlot where hubname = hname and slotno > newnslots;
 +      return 0;
 +    end if;
 +    for i in oldnslots + 1 .. newnslots loop
 +        insert into HSlot (slotname, hubname, slotno, slotlink)
 +              values (''HS.dummy'', hname, i, '''');
 +    end loop;
 +    return 0;
 +end
 +' language plpgsql;
 +-- Test comments
 +COMMENT ON FUNCTION tg_hub_adjustslots_wrong(bpchar, integer, integer) IS 'function with args';
 +ERROR:  function tg_hub_adjustslots_wrong(character, integer, integer) does not exist
 +COMMENT ON FUNCTION tg_hub_adjustslots(bpchar, integer, integer) IS 'function with args';
 +COMMENT ON FUNCTION tg_hub_adjustslots(bpchar, integer, integer) IS NULL;
 +-- ************************************************************
 +-- * BEFORE INSERT or UPDATE on HSlot
 +-- *  - prevent from manual manipulation
 +-- *  - set the slotname to HS.hubname.slotno
 +-- ************************************************************
 +create function tg_hslot_biu() returns trigger as '
 +declare
 +    sname     text;
 +    xname     HSlot.slotname%TYPE;
 +    hubrec    record;
 +begin
 +    select into hubrec * from Hub where name = new.hubname;
 +    if not found then
 +        raise exception ''no manual manipulation of HSlot'';
 +    end if;
 +    if new.slotno < 1 or new.slotno > hubrec.nslots then
 +        raise exception ''no manual manipulation of HSlot'';
 +    end if;
 +    if tg_op = ''UPDATE'' and new.hubname != old.hubname then
 +      if count(*) > 0 from Hub where name = old.hubname then
 +          raise exception ''no manual manipulation of HSlot'';
 +      end if;
 +    end if;
 +    sname := ''HS.'' || trim(new.hubname);
 +    sname := sname || ''.'';
 +    sname := sname || new.slotno::text;
 +    if length(sname) > 20 then
 +        raise exception ''HSlot slotname "%" too long (20 char max)'', sname;
 +    end if;
 +    new.slotname := sname;
 +    return new;
 +end;
 +' language plpgsql;
 +create trigger tg_hslot_biu before insert or update
 +    on HSlot for each row execute procedure tg_hslot_biu();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * BEFORE DELETE on HSlot
 +-- *  - prevent from manual manipulation
 +-- ************************************************************
 +create function tg_hslot_bd() returns trigger as '
 +declare
 +    hubrec    record;
 +begin
 +    select into hubrec * from Hub where name = old.hubname;
 +    if not found then
 +        return old;
 +    end if;
 +    if old.slotno > hubrec.nslots then
 +        return old;
 +    end if;
 +    raise exception ''no manual manipulation of HSlot'';
 +end;
 +' language plpgsql;
 +create trigger tg_hslot_bd before delete
 +    on HSlot for each row execute procedure tg_hslot_bd();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * BEFORE INSERT on all slots
 +-- *  - Check name prefix
 +-- ************************************************************
 +create function tg_chkslotname() returns trigger as '
 +begin
 +    if substr(new.slotname, 1, 2) != tg_argv[0] then
 +        raise exception ''slotname must begin with %'', tg_argv[0];
 +    end if;
 +    return new;
 +end;
 +' language plpgsql;
 +create trigger tg_chkslotname before insert
 +    on PSlot for each row execute procedure tg_chkslotname('PS');
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +create trigger tg_chkslotname before insert
 +    on WSlot for each row execute procedure tg_chkslotname('WS');
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +create trigger tg_chkslotname before insert
 +    on PLine for each row execute procedure tg_chkslotname('PL');
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +create trigger tg_chkslotname before insert
 +    on IFace for each row execute procedure tg_chkslotname('IF');
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +create trigger tg_chkslotname before insert
 +    on PHone for each row execute procedure tg_chkslotname('PH');
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * BEFORE INSERT or UPDATE on all slots with slotlink
 +-- *  - Set slotlink to empty string if NULL value given
 +-- ************************************************************
 +create function tg_chkslotlink() returns trigger as '
 +begin
 +    if new.slotlink isnull then
 +        new.slotlink := '''';
 +    end if;
 +    return new;
 +end;
 +' language plpgsql;
 +create trigger tg_chkslotlink before insert or update
 +    on PSlot for each row execute procedure tg_chkslotlink();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +create trigger tg_chkslotlink before insert or update
 +    on WSlot for each row execute procedure tg_chkslotlink();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +create trigger tg_chkslotlink before insert or update
 +    on IFace for each row execute procedure tg_chkslotlink();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +create trigger tg_chkslotlink before insert or update
 +    on HSlot for each row execute procedure tg_chkslotlink();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +create trigger tg_chkslotlink before insert or update
 +    on PHone for each row execute procedure tg_chkslotlink();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * BEFORE INSERT or UPDATE on all slots with backlink
 +-- *  - Set backlink to empty string if NULL value given
 +-- ************************************************************
 +create function tg_chkbacklink() returns trigger as '
 +begin
 +    if new.backlink isnull then
 +        new.backlink := '''';
 +    end if;
 +    return new;
 +end;
 +' language plpgsql;
 +create trigger tg_chkbacklink before insert or update
 +    on PSlot for each row execute procedure tg_chkbacklink();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +create trigger tg_chkbacklink before insert or update
 +    on WSlot for each row execute procedure tg_chkbacklink();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +create trigger tg_chkbacklink before insert or update
 +    on PLine for each row execute procedure tg_chkbacklink();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * BEFORE UPDATE on PSlot
 +-- *  - do delete/insert instead of update if name changes
 +-- ************************************************************
 +create function tg_pslot_bu() returns trigger as '
 +begin
 +    if new.slotname != old.slotname then
 +        delete from PSlot where slotname = old.slotname;
 +      insert into PSlot (
 +                  slotname,
 +                  pfname,
 +                  slotlink,
 +                  backlink
 +              ) values (
 +                  new.slotname,
 +                  new.pfname,
 +                  new.slotlink,
 +                  new.backlink
 +              );
 +        return null;
 +    end if;
 +    return new;
 +end;
 +' language plpgsql;
 +create trigger tg_pslot_bu before update
 +    on PSlot for each row execute procedure tg_pslot_bu();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * BEFORE UPDATE on WSlot
 +-- *  - do delete/insert instead of update if name changes
 +-- ************************************************************
 +create function tg_wslot_bu() returns trigger as '
 +begin
 +    if new.slotname != old.slotname then
 +        delete from WSlot where slotname = old.slotname;
 +      insert into WSlot (
 +                  slotname,
 +                  roomno,
 +                  slotlink,
 +                  backlink
 +              ) values (
 +                  new.slotname,
 +                  new.roomno,
 +                  new.slotlink,
 +                  new.backlink
 +              );
 +        return null;
 +    end if;
 +    return new;
 +end;
 +' language plpgsql;
 +create trigger tg_wslot_bu before update
 +    on WSlot for each row execute procedure tg_Wslot_bu();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * BEFORE UPDATE on PLine
 +-- *  - do delete/insert instead of update if name changes
 +-- ************************************************************
 +create function tg_pline_bu() returns trigger as '
 +begin
 +    if new.slotname != old.slotname then
 +        delete from PLine where slotname = old.slotname;
 +      insert into PLine (
 +                  slotname,
 +                  phonenumber,
 +                  comment,
 +                  backlink
 +              ) values (
 +                  new.slotname,
 +                  new.phonenumber,
 +                  new.comment,
 +                  new.backlink
 +              );
 +        return null;
 +    end if;
 +    return new;
 +end;
 +' language plpgsql;
 +create trigger tg_pline_bu before update
 +    on PLine for each row execute procedure tg_pline_bu();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * BEFORE UPDATE on IFace
 +-- *  - do delete/insert instead of update if name changes
 +-- ************************************************************
 +create function tg_iface_bu() returns trigger as '
 +begin
 +    if new.slotname != old.slotname then
 +        delete from IFace where slotname = old.slotname;
 +      insert into IFace (
 +                  slotname,
 +                  sysname,
 +                  ifname,
 +                  slotlink
 +              ) values (
 +                  new.slotname,
 +                  new.sysname,
 +                  new.ifname,
 +                  new.slotlink
 +              );
 +        return null;
 +    end if;
 +    return new;
 +end;
 +' language plpgsql;
 +create trigger tg_iface_bu before update
 +    on IFace for each row execute procedure tg_iface_bu();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * BEFORE UPDATE on HSlot
 +-- *  - do delete/insert instead of update if name changes
 +-- ************************************************************
 +create function tg_hslot_bu() returns trigger as '
 +begin
 +    if new.slotname != old.slotname or new.hubname != old.hubname then
 +        delete from HSlot where slotname = old.slotname;
 +      insert into HSlot (
 +                  slotname,
 +                  hubname,
 +                  slotno,
 +                  slotlink
 +              ) values (
 +                  new.slotname,
 +                  new.hubname,
 +                  new.slotno,
 +                  new.slotlink
 +              );
 +        return null;
 +    end if;
 +    return new;
 +end;
 +' language plpgsql;
 +create trigger tg_hslot_bu before update
 +    on HSlot for each row execute procedure tg_hslot_bu();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * BEFORE UPDATE on PHone
 +-- *  - do delete/insert instead of update if name changes
 +-- ************************************************************
 +create function tg_phone_bu() returns trigger as '
 +begin
 +    if new.slotname != old.slotname then
 +        delete from PHone where slotname = old.slotname;
 +      insert into PHone (
 +                  slotname,
 +                  comment,
 +                  slotlink
 +              ) values (
 +                  new.slotname,
 +                  new.comment,
 +                  new.slotlink
 +              );
 +        return null;
 +    end if;
 +    return new;
 +end;
 +' language plpgsql;
 +create trigger tg_phone_bu before update
 +    on PHone for each row execute procedure tg_phone_bu();
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * AFTER INSERT or UPDATE or DELETE on slot with backlink
 +-- *  - Ensure that the opponent correctly points back to us
 +-- ************************************************************
 +create function tg_backlink_a() returns trigger as '
 +declare
 +    dummy     integer;
 +begin
 +    if tg_op = ''INSERT'' then
 +        if new.backlink != '''' then
 +          dummy := tg_backlink_set(new.backlink, new.slotname);
 +      end if;
 +      return new;
 +    end if;
 +    if tg_op = ''UPDATE'' then
 +        if new.backlink != old.backlink then
 +          if old.backlink != '''' then
 +              dummy := tg_backlink_unset(old.backlink, old.slotname);
 +          end if;
 +          if new.backlink != '''' then
 +              dummy := tg_backlink_set(new.backlink, new.slotname);
 +          end if;
 +      else
 +          if new.slotname != old.slotname and new.backlink != '''' then
 +              dummy := tg_slotlink_set(new.backlink, new.slotname);
 +          end if;
 +      end if;
 +      return new;
 +    end if;
 +    if tg_op = ''DELETE'' then
 +        if old.backlink != '''' then
 +          dummy := tg_backlink_unset(old.backlink, old.slotname);
 +      end if;
 +      return old;
 +    end if;
 +end;
 +' language plpgsql;
 +create trigger tg_backlink_a after insert or update or delete
 +    on PSlot for each row execute procedure tg_backlink_a('PS');
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +create trigger tg_backlink_a after insert or update or delete
 +    on WSlot for each row execute procedure tg_backlink_a('WS');
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +create trigger tg_backlink_a after insert or update or delete
 +    on PLine for each row execute procedure tg_backlink_a('PL');
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * Support function to set the opponents backlink field
 +-- * if it does not already point to the requested slot
 +-- ************************************************************
 +create function tg_backlink_set(myname bpchar, blname bpchar)
 +returns integer as '
 +declare
 +    mytype    char(2);
 +    link      char(4);
 +    rec               record;
 +begin
 +    mytype := substr(myname, 1, 2);
 +    link := mytype || substr(blname, 1, 2);
 +    if link = ''PLPL'' then
 +        raise exception
 +              ''backlink between two phone lines does not make sense'';
 +    end if;
 +    if link in (''PLWS'', ''WSPL'') then
 +        raise exception
 +              ''direct link of phone line to wall slot not permitted'';
 +    end if;
 +    if mytype = ''PS'' then
 +        select into rec * from PSlot where slotname = myname;
 +      if not found then
 +          raise exception ''% does not exist'', myname;
 +      end if;
 +      if rec.backlink != blname then
 +          update PSlot set backlink = blname where slotname = myname;
 +      end if;
 +      return 0;
 +    end if;
 +    if mytype = ''WS'' then
 +        select into rec * from WSlot where slotname = myname;
 +      if not found then
 +          raise exception ''% does not exist'', myname;
 +      end if;
 +      if rec.backlink != blname then
 +          update WSlot set backlink = blname where slotname = myname;
 +      end if;
 +      return 0;
 +    end if;
 +    if mytype = ''PL'' then
 +        select into rec * from PLine where slotname = myname;
 +      if not found then
 +          raise exception ''% does not exist'', myname;
 +      end if;
 +      if rec.backlink != blname then
 +          update PLine set backlink = blname where slotname = myname;
 +      end if;
 +      return 0;
 +    end if;
 +    raise exception ''illegal backlink beginning with %'', mytype;
 +end;
 +' language plpgsql;
 +-- ************************************************************
 +-- * Support function to clear out the backlink field if
 +-- * it still points to specific slot
 +-- ************************************************************
 +create function tg_backlink_unset(bpchar, bpchar)
 +returns integer as '
 +declare
 +    myname    alias for $1;
 +    blname    alias for $2;
 +    mytype    char(2);
 +    rec               record;
 +begin
 +    mytype := substr(myname, 1, 2);
 +    if mytype = ''PS'' then
 +        select into rec * from PSlot where slotname = myname;
 +      if not found then
 +          return 0;
 +      end if;
 +      if rec.backlink = blname then
 +          update PSlot set backlink = '''' where slotname = myname;
 +      end if;
 +      return 0;
 +    end if;
 +    if mytype = ''WS'' then
 +        select into rec * from WSlot where slotname = myname;
 +      if not found then
 +          return 0;
 +      end if;
 +      if rec.backlink = blname then
 +          update WSlot set backlink = '''' where slotname = myname;
 +      end if;
 +      return 0;
 +    end if;
 +    if mytype = ''PL'' then
 +        select into rec * from PLine where slotname = myname;
 +      if not found then
 +          return 0;
 +      end if;
 +      if rec.backlink = blname then
 +          update PLine set backlink = '''' where slotname = myname;
 +      end if;
 +      return 0;
 +    end if;
 +end
 +' language plpgsql;
 +-- ************************************************************
 +-- * AFTER INSERT or UPDATE or DELETE on slot with slotlink
 +-- *  - Ensure that the opponent correctly points back to us
 +-- ************************************************************
 +create function tg_slotlink_a() returns trigger as '
 +declare
 +    dummy     integer;
 +begin
 +    if tg_op = ''INSERT'' then
 +        if new.slotlink != '''' then
 +          dummy := tg_slotlink_set(new.slotlink, new.slotname);
 +      end if;
 +      return new;
 +    end if;
 +    if tg_op = ''UPDATE'' then
 +        if new.slotlink != old.slotlink then
 +          if old.slotlink != '''' then
 +              dummy := tg_slotlink_unset(old.slotlink, old.slotname);
 +          end if;
 +          if new.slotlink != '''' then
 +              dummy := tg_slotlink_set(new.slotlink, new.slotname);
 +          end if;
 +      else
 +          if new.slotname != old.slotname and new.slotlink != '''' then
 +              dummy := tg_slotlink_set(new.slotlink, new.slotname);
 +          end if;
 +      end if;
 +      return new;
 +    end if;
 +    if tg_op = ''DELETE'' then
 +        if old.slotlink != '''' then
 +          dummy := tg_slotlink_unset(old.slotlink, old.slotname);
 +      end if;
 +      return old;
 +    end if;
 +end;
 +' language plpgsql;
 +create trigger tg_slotlink_a after insert or update or delete
 +    on PSlot for each row execute procedure tg_slotlink_a('PS');
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +create trigger tg_slotlink_a after insert or update or delete
 +    on WSlot for each row execute procedure tg_slotlink_a('WS');
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +create trigger tg_slotlink_a after insert or update or delete
 +    on IFace for each row execute procedure tg_slotlink_a('IF');
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +create trigger tg_slotlink_a after insert or update or delete
 +    on HSlot for each row execute procedure tg_slotlink_a('HS');
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +create trigger tg_slotlink_a after insert or update or delete
 +    on PHone for each row execute procedure tg_slotlink_a('PH');
 +ERROR:  Postgres-XL does not support TRIGGER yet
 +DETAIL:  The feature is not currently supported
 +-- ************************************************************
 +-- * Support function to set the opponents slotlink field
 +-- * if it does not already point to the requested slot
 +-- ************************************************************
 +create function tg_slotlink_set(bpchar, bpchar)
 +returns integer as '
 +declare
 +    myname    alias for $1;
 +    blname    alias for $2;
 +    mytype    char(2);
 +    link      char(4);
 +    rec               record;
 +begin
 +    mytype := substr(myname, 1, 2);
 +    link := mytype || substr(blname, 1, 2);
 +    if link = ''PHPH'' then
 +        raise exception
 +              ''slotlink between two phones does not make sense'';
 +    end if;
 +    if link in (''PHHS'', ''HSPH'') then
 +        raise exception
 +              ''link of phone to hub does not make sense'';
 +    end if;
 +    if link in (''PHIF'', ''IFPH'') then
 +        raise exception
 +              ''link of phone to hub does not make sense'';
 +    end if;
 +    if link in (''PSWS'', ''WSPS'') then
 +        raise exception
 +              ''slotlink from patchslot to wallslot not permitted'';
 +    end if;
 +    if mytype = ''PS'' then
 +        select into rec * from PSlot where slotname = myname;
 +      if not found then
 +          raise exception ''% does not exist'', myname;
 +      end if;
 +      if rec.slotlink != blname then
 +          update PSlot set slotlink = blname where slotname = myname;
 +      end if;
 +      return 0;
 +    end if;
 +    if mytype = ''WS'' then
 +        select into rec * from WSlot where slotname = myname;
 +      if not found then
 +          raise exception ''% does not exist'', myname;
 +      end if;
 +      if rec.slotlink != blname then
 +          update WSlot set slotlink = blname where slotname = myname;
 +      end if;
 +      return 0;
 +    end if;
 +    if mytype = ''IF'' then
 +        select into rec * from IFace where slotname = myname;
 +      if not found then
 +          raise exception ''% does not exist'', myname;
 +      end if;
 +      if rec.slotlink != blname then
 +          update IFace set slotlink = blname where slotname = myname;
 +      end if;
 +      return 0;
 +    end if;
 +    if mytype = ''HS'' then
 +        select into rec * from HSlot where slotname = myname;
 +      if not found then
 +          raise exception ''% does not exist'', myname;
 +      end if;
 +      if rec.slotlink != blname then
 +          update HSlot set slotlink = blname where slotname = myname;
 +      end if;
 +      return 0;
 +    end if;
 +    if mytype = ''PH'' then
 +        select into rec * from PHone where slotname = myname;
 +      if not found then
 +          raise exception ''% does not exist'', myname;
 +      end if;
 +      if rec.slotlink != blname then
 +          update PHone set slotlink = blname where slotname = myname;
 +      end if;
 +      return 0;
 +    end if;
 +    raise exception ''illegal slotlink beginning with %'', mytype;
 +end;
 +' language plpgsql;
 +-- ************************************************************
 +-- * Support function to clear out the slotlink field if
 +-- * it still points to specific slot
 +-- ************************************************************
 +create function tg_slotlink_unset(bpchar, bpchar)
 +returns integer as '
 +declare
 +    myname    alias for $1;
 +    blname    alias for $2;
 +    mytype    char(2);
 +    rec               record;
 +begin
 +    mytype := substr(myname, 1, 2);
 +    if mytype = ''PS'' then
 +        select into rec * from PSlot where slotname = myname;
 +      if not found then
 +          return 0;
 +      end if;
 +      if rec.slotlink = blname then
 +          update PSlot set slotlink = '''' where slotname = myname;
 +      end if;
 +      return 0;
 +    end if;
 +    if mytype = ''WS'' then
 +        select into rec * from WSlot where slotname = myname;
 +      if not found then
 +          return 0;
 +      end if;
 +      if rec.slotlink = blname then
 +          update WSlot set slotlink = '''' where slotname = myname;
 +      end if;
 +      return 0;
 +    end if;
 +    if mytype = ''IF'' then
 +        select into rec * from IFace where slotname = myname;
 +      if not found then
 +          return 0;
 +      end if;
 +      if rec.slotlink = blname then
 +          update IFace set slotlink = '''' where slotname = myname;
 +      end if;
 +      return 0;
 +    end if;
 +    if mytype = ''HS'' then
 +        select into rec * from HSlot where slotname = myname;
 +      if not found then
 +          return 0;
 +      end if;
 +      if rec.slotlink = blname then
 +          update HSlot set slotlink = '''' where slotname = myname;
 +      end if;
 +      return 0;
 +    end if;
 +    if mytype = ''PH'' then
 +        select into rec * from PHone where slotname = myname;
 +      if not found then
 +          return 0;
 +      end if;
 +      if rec.slotlink = blname then
 +          update PHone set slotlink = '''' where slotname = myname;
 +      end if;
 +      return 0;
 +    end if;
 +end;
 +' language plpgsql;
 +-- ************************************************************
 +-- * Describe the backside of a patchfield slot
 +-- ************************************************************
 +create function pslot_backlink_view(bpchar)
 +returns text as '
 +<<outer>>
 +declare
 +    rec               record;
 +    bltype    char(2);
 +    retval    text;
 +begin
 +    select into rec * from PSlot where slotname = $1;
 +    if not found then
 +        return '''';
 +    end if;
 +    if rec.backlink = '''' then
 +        return ''-'';
 +    end if;
 +    bltype := substr(rec.backlink, 1, 2);
 +    if bltype = ''PL'' then
 +        declare
 +          rec         record;
 +      begin
 +          select into rec * from PLine where slotname = "outer".rec.backlink;
 +          retval := ''Phone line '' || trim(rec.phonenumber);
 +          if rec.comment != '''' then
 +              retval := retval || '' ('';
 +              retval := retval || rec.comment;
 +              retval := retval || '')'';
 +          end if;
 +          return retval;
 +      end;
 +    end if;
 +    if bltype = ''WS'' then
 +        select into rec * from WSlot where slotname = rec.backlink;
 +      retval := trim(rec.slotname) || '' in room '';
 +      retval := retval || trim(rec.roomno);
 +      retval := retval || '' -> '';
 +      return retval || wslot_slotlink_view(rec.slotname);
 +    end if;
 +    return rec.backlink;
 +end;
 +' language plpgsql;
 +-- ************************************************************
 +-- * Describe the front of a patchfield slot
 +-- ************************************************************
 +create function pslot_slotlink_view(bpchar)
 +returns text as '
 +declare
 +    psrec     record;
 +    sltype    char(2);
 +    retval    text;
 +begin
 +    select into psrec * from PSlot where slotname = $1;
 +    if not found then
 +        return '''';
 +    end if;
 +    if psrec.slotlink = '''' then
 +        return ''-'';
 +    end if;
 +    sltype := substr(psrec.slotlink, 1, 2);
 +    if sltype = ''PS'' then
 +      retval := trim(psrec.slotlink) || '' -> '';
 +      return retval || pslot_backlink_view(psrec.slotlink);
 +    end if;
 +    if sltype = ''HS'' then
 +        retval := comment from Hub H, HSlot HS
 +                      where HS.slotname = psrec.slotlink
 +                        and H.name = HS.hubname;
 +        retval := retval || '' slot '';
 +      retval := retval || slotno::text from HSlot
 +                      where slotname = psrec.slotlink;
 +      return retval;
 +    end if;
 +    return psrec.slotlink;
 +end;
 +' language plpgsql;
 +-- ************************************************************
 +-- * Describe the front of a wall connector slot
 +-- ************************************************************
 +create function wslot_slotlink_view(bpchar)
 +returns text as '
 +declare
 +    rec               record;
 +    sltype    char(2);
 +    retval    text;
 +begin
 +    select into rec * from WSlot where slotname = $1;
 +    if not found then
 +        return '''';
 +    end if;
 +    if rec.slotlink = '''' then
 +        return ''-'';
 +    end if;
 +    sltype := substr(rec.slotlink, 1, 2);
 +    if sltype = ''PH'' then
 +        select into rec * from PHone where slotname = rec.slotlink;
 +      retval := ''Phone '' || trim(rec.slotname);
 +      if rec.comment != '''' then
 +          retval := retval || '' ('';
 +          retval := retval || rec.comment;
 +          retval := retval || '')'';
 +      end if;
 +      return retval;
 +    end if;
 +    if sltype = ''IF'' then
 +      declare
 +          syrow       System%RowType;
 +          ifrow       IFace%ROWTYPE;
 +        begin
 +          select into ifrow * from IFace where slotname = rec.slotlink;
 +          select into syrow * from System where name = ifrow.sysname;
 +          retval := syrow.name || '' IF '';
 +          retval := retval || ifrow.ifname;
 +          if syrow.comment != '''' then
 +              retval := retval || '' ('';
 +              retval := retval || syrow.comment;
 +              retval := retval || '')'';
 +          end if;
 +          return retval;
 +      end;
 +    end if;
 +    return rec.slotlink;
 +end;
 +' language plpgsql;
 +-- ************************************************************
 +-- * View of a patchfield describing backside and patches
 +-- ************************************************************
 +create view Pfield_v1 as select PF.pfname, PF.slotname,
 +      pslot_backlink_view(PF.slotname) as backside,
 +      pslot_slotlink_view(PF.slotname) as patch
 +    from PSlot PF;
 +--
 +-- First we build the house - so we create the rooms
 +--
 +insert into Room values ('001', 'Entrance');
 +insert into Room values ('002', 'Office');
 +insert into Room values ('003', 'Office');
 +insert into Room values ('004', 'Technical');
 +insert into Room values ('101', 'Office');
 +insert into Room values ('102', 'Conference');
 +insert into Room values ('103', 'Restroom');
 +insert into Room values ('104', 'Technical');
 +insert into Room values ('105', 'Office');
 +insert into Room values ('106', 'Office');
 +--
 +-- Second we install the wall connectors
 +--
 +insert into WSlot values ('WS.001.1a', '001', '', '');
 +insert into WSlot values ('WS.001.1b', '001', '', '');
 +insert into WSlot values ('WS.001.2a', '001', '', '');
 +insert into WSlot values ('WS.001.2b', '001', '', '');
 +insert into WSlot values ('WS.001.3a', '001', '', '');
 +insert into WSlot values ('WS.001.3b', '001', '', '');
 +insert into WSlot values ('WS.002.1a', '002', '', '');
 +insert into WSlot values ('WS.002.1b', '002', '', '');
 +insert into WSlot values ('WS.002.2a', '002', '', '');
 +insert into WSlot values ('WS.002.2b', '002', '', '');
 +insert into WSlot values ('WS.002.3a', '002', '', '');
 +insert into WSlot values ('WS.002.3b', '002', '', '');
 +insert into WSlot values ('WS.003.1a', '003', '', '');
 +insert into WSlot values ('WS.003.1b', '003', '', '');
 +insert into WSlot values ('WS.003.2a', '003', '', '');
 +insert into WSlot values ('WS.003.2b', '003', '', '');
 +insert into WSlot values ('WS.003.3a', '003', '', '');
 +insert into WSlot values ('WS.003.3b', '003', '', '');
 +insert into WSlot values ('WS.101.1a', '101', '', '');
 +insert into WSlot values ('WS.101.1b', '101', '', '');
 +insert into WSlot values ('WS.101.2a', '101', '', '');
 +insert into WSlot values ('WS.101.2b', '101', '', '');
 +insert into WSlot values ('WS.101.3a', '101', '', '');
 +insert into WSlot values ('WS.101.3b', '101', '', '');
 +insert into WSlot values ('WS.102.1a', '102', '', '');
 +insert into WSlot values ('WS.102.1b', '102', '', '');
 +insert into WSlot values ('WS.102.2a', '102', '', '');
 +insert into WSlot values ('WS.102.2b', '102', '', '');
 +insert into WSlot values ('WS.102.3a', '102', '', '');
 +insert into WSlot values ('WS.102.3b', '102', '', '');
 +insert into WSlot values ('WS.105.1a', '105', '', '');
 +insert into WSlot values ('WS.105.1b', '105', '', '');
 +insert into WSlot values ('WS.105.2a', '105', '', '');
 +insert into WSlot values ('WS.105.2b', '105', '', '');
 +insert into WSlot values ('WS.105.3a', '105', '', '');
 +insert into WSlot values ('WS.105.3b', '105', '', '');
 +insert into WSlot values ('WS.106.1a', '106', '', '');
 +insert into WSlot values ('WS.106.1b', '106', '', '');
 +insert into WSlot values ('WS.106.2a', '106', '', '');
 +insert into WSlot values ('WS.106.2b', '106', '', '');
 +insert into WSlot values ('WS.106.3a', '106', '', '');
 +insert into WSlot values ('WS.106.3b', '106', '', '');
 +--
 +-- Now create the patch fields and their slots
 +--
 +insert into PField values ('PF0_1', 'Wallslots basement');
 +--
 +-- The cables for these will be made later, so they are unconnected for now
 +--
 +insert into PSlot values ('PS.base.a1', 'PF0_1', '', '');
 +insert into PSlot values ('PS.base.a2', 'PF0_1', '', '');
 +insert into PSlot values ('PS.base.a3', 'PF0_1', '', '');
 +insert into PSlot values ('PS.base.a4', 'PF0_1', '', '');
 +insert into PSlot values ('PS.base.a5', 'PF0_1', '', '');
 +insert into PSlot values ('PS.base.a6', 'PF0_1', '', '');
 +--
 +-- These are already wired to the wall connectors
 +--
 +insert into PSlot values ('PS.base.b1', 'PF0_1', '', 'WS.002.1a');
 +insert into PSlot values ('PS.base.b2', 'PF0_1', '', 'WS.002.1b');
 +insert into PSlot values ('PS.base.b3', 'PF0_1', '', 'WS.002.2a');
 +insert into PSlot values ('PS.base.b4', 'PF0_1', '', 'WS.002.2b');
 +insert into PSlot values ('PS.base.b5', 'PF0_1', '', 'WS.002.3a');
 +insert into PSlot values ('PS.base.b6', 'PF0_1', '', 'WS.002.3b');
 +insert into PSlot values ('PS.base.c1', 'PF0_1', '', 'WS.003.1a');
 +insert into PSlot values ('PS.base.c2', 'PF0_1', '', 'WS.003.1b');
 +insert into PSlot values ('PS.base.c3', 'PF0_1', '', 'WS.003.2a');
 +insert into PSlot values ('PS.base.c4', 'PF0_1', '', 'WS.003.2b');
 +insert into PSlot values ('PS.base.c5', 'PF0_1', '', 'WS.003.3a');
 +insert into PSlot values ('PS.base.c6', 'PF0_1', '', 'WS.003.3b');
 +--
 +-- This patchfield will be renamed later into PF0_2 - so its
 +-- slots references in pfname should follow
 +--
 +insert into PField values ('PF0_X', 'Phonelines basement');
 +insert into PSlot values ('PS.base.ta1', 'PF0_X', '', '');
 +insert into PSlot values ('PS.base.ta2', 'PF0_X', '', '');
 +insert into PSlot values ('PS.base.ta3', 'PF0_X', '', '');
 +insert into PSlot values ('PS.base.ta4', 'PF0_X', '', '');
 +insert into PSlot values ('PS.base.ta5', 'PF0_X', '', '');
 +insert into PSlot values ('PS.base.ta6', 'PF0_X', '', '');
 +insert into PSlot values ('PS.base.tb1', 'PF0_X', '', '');
 +insert into PSlot values ('PS.base.tb2', 'PF0_X', '', '');
 +insert into PSlot values ('PS.base.tb3', 'PF0_X', '', '');
 +insert into PSlot values ('PS.base.tb4', 'PF0_X', '', '');
 +insert into PSlot values ('PS.base.tb5', 'PF0_X', '', '');
 +insert into PSlot values ('PS.base.tb6', 'PF0_X', '', '');
 +insert into PField values ('PF1_1', 'Wallslots first floor');
 +insert into PSlot values ('PS.first.a1', 'PF1_1', '', 'WS.101.1a');
 +insert into PSlot values ('PS.first.a2', 'PF1_1', '', 'WS.101.1b');
 +insert into PSlot values ('PS.first.a3', 'PF1_1', '', 'WS.101.2a');
 +insert into PSlot values ('PS.first.a4', 'PF1_1', '', 'WS.101.2b');
 +insert into PSlot values ('PS.first.a5', 'PF1_1', '', 'WS.101.3a');
 +insert into PSlot values ('PS.first.a6', 'PF1_1', '', 'WS.101.3b');
 +insert into PSlot values ('PS.first.b1', 'PF1_1', '', 'WS.102.1a');
 +insert into PSlot values ('PS.first.b2', 'PF1_1', '', 'WS.102.1b');
 +insert into PSlot values ('PS.first.b3', 'PF1_1', '', 'WS.102.2a');
 +insert into PSlot values ('PS.first.b4', 'PF1_1', '', 'WS.102.2b');
 +insert into PSlot values ('PS.first.b5', 'PF1_1', '', 'WS.102.3a');
 +insert into PSlot values ('PS.first.b6', 'PF1_1', '', 'WS.102.3b');
 +insert into PSlot values ('PS.first.c1', 'PF1_1', '', 'WS.105.1a');
 +insert into PSlot values ('PS.first.c2', 'PF1_1', '', 'WS.105.1b');
 +insert into PSlot values ('PS.first.c3', 'PF1_1', '', 'WS.105.2a');
 +insert into PSlot values ('PS.first.c4', 'PF1_1', '', 'WS.105.2b');
 +insert into PSlot values ('PS.first.c5', 'PF1_1', '', 'WS.105.3a');
 +insert into PSlot values ('PS.first.c6', 'PF1_1', '', 'WS.105.3b');
 +insert into PSlot values ('PS.first.d1', 'PF1_1', '', 'WS.106.1a');
 +insert into PSlot values ('PS.first.d2', 'PF1_1', '', 'WS.106.1b');
 +insert into PSlot values ('PS.first.d3', 'PF1_1', '', 'WS.106.2a');
 +insert into PSlot values ('PS.first.d4', 'PF1_1', '', 'WS.106.2b');
 +insert into PSlot values ('PS.first.d5', 'PF1_1', '', 'WS.106.3a');
 +insert into PSlot values ('PS.first.d6', 'PF1_1', '', 'WS.106.3b');
 +--
 +-- Now we wire the wall connectors 1a-2a in room 001 to the
 +-- patchfield. In the second update we make an error, and
 +-- correct it after
 +--
 +update PSlot set backlink = 'WS.001.1a' where slotname = 'PS.base.a1';
 +update PSlot set backlink = 'WS.001.1b' where slotname = 'PS.base.a3';
 +select * from WSlot where roomno = '001' order by slotname;
 +       slotname       |  roomno  |       slotlink       |       backlink       
 +----------------------+----------+----------------------+----------------------
 + WS.001.1a            | 001      |                      |                     
 + WS.001.1b            | 001      |                      |                     
 + WS.001.2a            | 001      |                      |                     
 + WS.001.2b            | 001      |                      |                     
 + WS.001.3a            | 001      |                      |                     
 + WS.001.3b            | 001      |                      |                     
 +(6 rows)
 +
 +select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
 +       slotname       | pfname |       slotlink       |       backlink       
 +----------------------+--------+----------------------+----------------------
 + PS.base.a1           | PF0_1  |                      | WS.001.1a           
 + PS.base.a2           | PF0_1  |                      |                     
 + PS.base.a3           | PF0_1  |                      | WS.001.1b           
 + PS.base.a4           | PF0_1  |                      |                     
 + PS.base.a5           | PF0_1  |                      |                     
 + PS.base.a6           | PF0_1  |                      |                     
 +(6 rows)
 +
 +update PSlot set backlink = 'WS.001.2a' where slotname = 'PS.base.a3';
 +select * from WSlot where roomno = '001' order by slotname;
 +       slotname       |  roomno  |       slotlink       |       backlink       
 +----------------------+----------+----------------------+----------------------
 + WS.001.1a            | 001      |                      |                     
 + WS.001.1b            | 001      |                      |                     
 + WS.001.2a            | 001      |                      |                     
 + WS.001.2b            | 001      |                      |                     
 + WS.001.3a            | 001      |                      |                     
 + WS.001.3b            | 001      |                      |                     
 +(6 rows)
 +
 +select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
 +       slotname       | pfname |       slotlink       |       backlink       
 +----------------------+--------+----------------------+----------------------
 + PS.base.a1           | PF0_1  |                      | WS.001.1a           
 + PS.base.a2           | PF0_1  |                      |                     
 + PS.base.a3           | PF0_1  |                      | WS.001.2a           
 + PS.base.a4           | PF0_1  |                      |                     
 + PS.base.a5           | PF0_1  |                      |                     
 + PS.base.a6           | PF0_1  |                      |                     
 +(6 rows)
 +
 +update PSlot set backlink = 'WS.001.1b' where slotname = 'PS.base.a2';
 +select * from WSlot where roomno = '001' order by slotname;
 +       slotname       |  roomno  |       slotlink       |       backlink       
 +----------------------+----------+----------------------+----------------------
 + WS.001.1a            | 001      |                      |                     
 + WS.001.1b            | 001      |                      |                     
 + WS.001.2a            | 001      |                      |                     
 + WS.001.2b            | 001      |                      |                     
 + WS.001.3a            | 001      |                      |                     
 + WS.001.3b            | 001      |                      |                     
 +(6 rows)
 +
 +select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
 +       slotname       | pfname |       slotlink       |       backlink       
 +----------------------+--------+----------------------+----------------------
 + PS.base.a1           | PF0_1  |                      | WS.001.1a           
 + PS.base.a2           | PF0_1  |                      | WS.001.1b           
 + PS.base.a3           | PF0_1  |                      | WS.001.2a           
 + PS.base.a4           | PF0_1  |                      |                     
 + PS.base.a5           | PF0_1  |                      |                     
 + PS.base.a6           | PF0_1  |                      |                     
 +(6 rows)
 +
 +--
 +-- Same procedure for 2b-3b but this time updating the WSlot instead
 +-- of the PSlot. Due to the triggers the result is the same:
 +-- WSlot and corresponding PSlot point to each other.
 +--
 +update WSlot set backlink = 'PS.base.a4' where slotname = 'WS.001.2b';
 +update WSlot set backlink = 'PS.base.a6' where slotname = 'WS.001.3a';
 +select * from WSlot where roomno = '001' order by slotname;
 +       slotname       |  roomno  |       slotlink       |       backlink       
 +----------------------+----------+----------------------+----------------------
 + WS.001.1a            | 001      |                      |                     
 + WS.001.1b            | 001      |                      |                     
 + WS.001.2a            | 001      |                      |                     
 + WS.001.2b            | 001      |                      | PS.base.a4          
 + WS.001.3a            | 001      |                      | PS.base.a6          
 + WS.001.3b            | 001      |                      |                     
 +(6 rows)
 +
 +select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
 +       slotname       | pfname |       slotlink       |       backlink       
 +----------------------+--------+----------------------+----------------------
 + PS.base.a1           | PF0_1  |                      | WS.001.1a           
 + PS.base.a2           | PF0_1  |                      | WS.001.1b           
 + PS.base.a3           | PF0_1  |                      | WS.001.2a           
 + PS.base.a4           | PF0_1  |                      |                     
 + PS.base.a5           | PF0_1  |                      |                     
 + PS.base.a6           | PF0_1  |                      |                     
 +(6 rows)
 +
 +update WSlot set backlink = 'PS.base.a6' where slotname = 'WS.001.3b';
 +select * from WSlot where roomno = '001' order by slotname;
 +       slotname       |  roomno  |       slotlink       |       backlink       
 +----------------------+----------+----------------------+----------------------
 + WS.001.1a            | 001      |                      |                     
 + WS.001.1b            | 001      |                      |                     
 + WS.001.2a            | 001      |                      |                     
 + WS.001.2b            | 001      |                      | PS.base.a4          
 + WS.001.3a            | 001      |                      | PS.base.a6          
 + WS.001.3b            | 001      |                      | PS.base.a6          
 +(6 rows)
 +
 +select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
 +       slotname       | pfname |       slotlink       |       backlink       
 +----------------------+--------+----------------------+----------------------
 + PS.base.a1           | PF0_1  |                      | WS.001.1a           
 + PS.base.a2           | PF0_1  |                      | WS.001.1b           
 + PS.base.a3           | PF0_1  |                      | WS.001.2a           
 + PS.base.a4           | PF0_1  |                      |                     
 + PS.base.a5           | PF0_1  |                      |                     
 + PS.base.a6           | PF0_1  |                      |                     
 +(6 rows)
 +
 +update WSlot set backlink = 'PS.base.a5' where slotname = 'WS.001.3a';
 +select * from WSlot where roomno = '001' order by slotname;
 +       slotname       |  roomno  |       slotlink       |       backlink       
 +----------------------+----------+----------------------+----------------------
 + WS.001.1a            | 001      |                      |                     
 + WS.001.1b            | 001      |                      |                     
 + WS.001.2a            | 001      |                      |                     
 + WS.001.2b            | 001      |                      | PS.base.a4          
 + WS.001.3a            | 001      |                      | PS.base.a5          
 + WS.001.3b            | 001      |                      | PS.base.a6          
 +(6 rows)
 +
 +select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
 +       slotname       | pfname |       slotlink       |       backlink       
 +----------------------+--------+----------------------+----------------------
 + PS.base.a1           | PF0_1  |                      | WS.001.1a           
 + PS.base.a2           | PF0_1  |                      | WS.001.1b           
 + PS.base.a3           | PF0_1  |                      | WS.001.2a           
 + PS.base.a4           | PF0_1  |                      |                     
 + PS.base.a5           | PF0_1  |                      |                     
 + PS.base.a6           | PF0_1  |                      |                     
 +(6 rows)
 +
 +insert into PField values ('PF1_2', 'Phonelines first floor');
 +insert into PSlot values ('PS.first.ta1', 'PF1_2', '', '');
 +insert into PSlot values ('PS.first.ta2', 'PF1_2', '', '');
 +insert into PSlot values ('PS.first.ta3', 'PF1_2', '', '');
 +insert into PSlot values ('PS.first.ta4', 'PF1_2', '', '');
 +insert into PSlot values ('PS.first.ta5', 'PF1_2', '', '');
 +insert into PSlot values ('PS.first.ta6', 'PF1_2', '', '');
 +insert into PSlot values ('PS.first.tb1', 'PF1_2', '', '');
 +insert into PSlot values ('PS.first.tb2', 'PF1_2', '', '');
 +insert into PSlot values ('PS.first.tb3', 'PF1_2', '', '');
 +insert into PSlot values ('PS.first.tb4', 'PF1_2', '', '');
 +insert into PSlot values ('PS.first.tb5', 'PF1_2', '', '');
 +insert into PSlot values ('PS.first.tb6', 'PF1_2', '', '');
 +--
 +-- Fix the wrong name for patchfield PF0_2
 +--
 +update PField set name = 'PF0_2' where name = 'PF0_X';
 +select * from PSlot order by slotname;
 +       slotname       | pfname |       slotlink       |       backlink       
 +----------------------+--------+----------------------+----------------------
 + PS.base.a1           | PF0_1  |                      | WS.001.1a           
 + PS.base.a2           | PF0_1  |                      | WS.001.1b           
 + PS.base.a3           | PF0_1  |                      | WS.001.2a           
 + PS.base.a4           | PF0_1  |                      |                     
 + PS.base.a5           | PF0_1  |                      |                     
 + PS.base.a6           | PF0_1  |                      |                     
 + PS.base.b1           | PF0_1  |                      | WS.002.1a           
 + PS.base.b2           | PF0_1  |                      | WS.002.1b           
 + PS.base.b3           | PF0_1  |                      | WS.002.2a           
 + PS.base.b4           | PF0_1  |                      | WS.002.2b           
 + PS.base.b5           | PF0_1  |                      | WS.002.3a           
 + PS.base.b6           | PF0_1  |                      | WS.002.3b           
 + PS.base.c1           | PF0_1  |                      | WS.003.1a           
 + PS.base.c2           | PF0_1  |                      | WS.003.1b           
 + PS.base.c3           | PF0_1  |                      | WS.003.2a           
 + PS.base.c4           | PF0_1  |                      | WS.003.2b           
 + PS.base.c5           | PF0_1  |                      | WS.003.3a           
 + PS.base.c6           | PF0_1  |                      | WS.003.3b           
 + PS.base.ta1          | PF0_X  |                      |                     
 + PS.base.ta2          | PF0_X  |                      |                     
 + PS.base.ta3          | PF0_X  |                      |                     
 + PS.base.ta4          | PF0_X  |                      |                     
 + PS.base.ta5          | PF0_X  |                      |                     
 + PS.base.ta6          | PF0_X  |                      |                     
 + PS.base.tb1          | PF0_X  |                      |                     
 + PS.base.tb2          | PF0_X  |                      |                     
 + PS.base.tb3          | PF0_X  |                      |                     
 + PS.base.tb4          | PF0_X  |                      |                     
 + PS.base.tb5          | PF0_X  |                      |                     
 + PS.base.tb6          | PF0_X  |                      |                     
 + PS.first.a1          | PF1_1  |                      | WS.101.1a           
 + PS.first.a2          | PF1_1  |                      | WS.101.1b           
 + PS.first.a3          | PF1_1  |                      | WS.101.2a           
 + PS.first.a4          | PF1_1  |                      | WS.101.2b           
 + PS.first.a5          | PF1_1  |                      | WS.101.3a           
 + PS.first.a6          | PF1_1  |                      | WS.101.3b           
 + PS.first.b1          | PF1_1  |                      | WS.102.1a           
 + PS.first.b2          | PF1_1  |                      | WS.102.1b           
 + PS.first.b3          | PF1_1  |                      | WS.102.2a           
 + PS.first.b4          | PF1_1  |                      | WS.102.2b           
 + PS.first.b5          | PF1_1  |                      | WS.102.3a           
 + PS.first.b6          | PF1_1  |                      | WS.102.3b           
 + PS.first.c1          | PF1_1  |                      | WS.105.1a           
 + PS.first.c2          | PF1_1  |                      | WS.105.1b           
 + PS.first.c3          | PF1_1  |                      | WS.105.2a           
 + PS.first.c4          | PF1_1  |                      | WS.105.2b           
 + PS.first.c5          | PF1_1  |                      | WS.105.3a           
 + PS.first.c6          | PF1_1  |                      | WS.105.3b           
 + PS.first.d1          | PF1_1  |                      | WS.106.1a           
 + PS.first.d2          | PF1_1  |                      | WS.106.1b           
 + PS.first.d3          | PF1_1  |                      | WS.106.2a           
 + PS.first.d4          | PF1_1  |                      | WS.106.2b           
 + PS.first.d5          | PF1_1  |                      | WS.106.3a           
 + PS.first.d6          | PF1_1  |                      | WS.106.3b           
 + PS.first.ta1         | PF1_2  |                      |                     
 + PS.first.ta2         | PF1_2  |                      |                     
 + PS.first.ta3         | PF1_2  |                      |                     
 + PS.first.ta4         | PF1_2  |                      |                     
 + PS.first.ta5         | PF1_2  |                      |                     
 + PS.first.ta6         | PF1_2  |                      |                     
 + PS.first.tb1         | PF1_2  |                      |                     
 + PS.first.tb2         | PF1_2  |                      |                     
 + PS.first.tb3         | PF1_2  |                      |                     
 + PS.first.tb4         | PF1_2  |                      |                     
 + PS.first.tb5         | PF1_2  |                      |                     
 + PS.first.tb6         | PF1_2  |                      |                     
 +(66 rows)
 +
 +select * from WSlot order by slotname;
 +       slotname       |  roomno  |       slotlink       |       backlink       
 +----------------------+----------+----------------------+----------------------
 + WS.001.1a            | 001      |                      |                     
 + WS.001.1b            | 001      |                      |                     
 + WS.001.2a            | 001      |                      |                     
 + WS.001.2b            | 001      |                      | PS.base.a4          
 + WS.001.3a            | 001      |                      | PS.base.a5          
 + WS.001.3b            | 001      |                      | PS.base.a6          
 + WS.002.1a            | 002      |                      |                     
 + WS.002.1b            | 002      |                      |                     
 + WS.002.2a            | 002      |                      |                     
 + WS.002.2b            | 002      |                      |                     
 + WS.002.3a            | 002      |                      |                     
 + WS.002.3b            | 002      |                      |                     
 + WS.003.1a            | 003      |                      |                     
 + WS.003.1b            | 003      |                      |                     
 + WS.003.2a            | 003      |                      |                     
 + WS.003.2b            | 003      |                      |                     
 + WS.003.3a            | 003      |                      |                     
 + WS.003.3b            | 003      |                      |                     
 + WS.101.1a            | 101      |                      |                     
 + WS.101.1b            | 101      |                      |                     
 + WS.101.2a            | 101      |                      |                     
 + WS.101.2b            | 101      |                      |                     
 + WS.101.3a            | 101      |                      |                     
 + WS.101.3b            | 101      |                      |                     
 + WS.102.1a            | 102      |                      |                     
 + WS.102.1b            | 102      |                      |                     
 + WS.102.2a            | 102      |                      |                     
 + WS.102.2b            | 102      |                      |                     
 + WS.102.3a            | 102      |                      |                     
 + WS.102.3b            | 102      |                      |                     
 + WS.105.1a            | 105      |                      |                     
 + WS.105.1b            | 105      |                      |                     
 + WS.105.2a            | 105      |                      |                     
 + WS.105.2b            | 105      |                      |                     
 + WS.105.3a            | 105      |                      |                     
 + WS.105.3b            | 105      |                      |                     
 + WS.106.1a            | 106      |                      |                     
 + WS.106.1b            | 106      |                      |                     
 + WS.106.2a            | 106      |                      |                     
 + WS.106.2b            | 106      |                      |                     
 + WS.106.3a            | 106      |                      |                     
 + WS.106.3b            | 106      |                      |                     
 +(42 rows)
 +
 +--
 +-- Install the central phone system and create the phone numbers.
 +-- They are weired on insert to the patchfields. Again the
 +-- triggers automatically tell the PSlots to update their
 +-- backlink field.
 +--
 +insert into PLine values ('PL.001', '-0', 'Central call', 'PS.base.ta1');
 +insert into PLine values ('PL.002', '-101', '', 'PS.base.ta2');
 +insert into PLine values ('PL.003', '-102', '', 'PS.base.ta3');
 +insert into PLine values ('PL.004', '-103', '', 'PS.base.ta5');
 +insert into PLine values ('PL.005', '-104', '', 'PS.base.ta6');
 +insert into PLine values ('PL.006', '-106', '', 'PS.base.tb2');
 +insert into PLine values ('PL.007', '-108', '', 'PS.base.tb3');
 +insert into PLine values ('PL.008', '-109', '', 'PS.base.tb4');
 +insert into PLine values ('PL.009', '-121', '', 'PS.base.tb5');
 +insert into PLine values ('PL.010', '-122', '', 'PS.base.tb6');
 +insert into PLine values ('PL.015', '-134', '', 'PS.first.ta1');
 +insert into PLine values ('PL.016', '-137', '', 'PS.first.ta3');
 +insert into PLine values ('PL.017', '-139', '', 'PS.first.ta4');
 +insert into PLine values ('PL.018', '-362', '', 'PS.first.tb1');
 +insert into PLine values ('PL.019', '-363', '', 'PS.first.tb2');
 +insert into PLine values ('PL.020', '-364', '', 'PS.first.tb3');
 +insert into PLine values ('PL.021', '-365', '', 'PS.first.tb5');
 +insert into PLine values ('PL.022', '-367', '', 'PS.first.tb6');
 +insert into PLine values ('PL.028', '-501', 'Fax entrance', 'PS.base.ta2');
 +insert into PLine values ('PL.029', '-502', 'Fax first floor', 'PS.first.ta1');
 +--
 +-- Buy some phones, plug them into the wall and patch the
 +-- phone lines to the corresponding patchfield slots.
 +--
 +insert into PHone values ('PH.hc001', 'Hicom standard', 'WS.001.1a');
 +update PSlot set slotlink = 'PS.base.ta1' where slotname = 'PS.base.a1';
 +insert into PHone values ('PH.hc002', 'Hicom standard', 'WS.002.1a');
 +update PSlot set slotlink = 'PS.base.ta5' where slotname = 'PS.base.b1';
 +insert into PHone values ('PH.hc003', 'Hicom standard', 'WS.002.2a');
 +update PSlot set slotlink = 'PS.base.tb2' where slotname = 'PS.base.b3';
 +insert into PHone values ('PH.fax001', 'Canon fax', 'WS.001.2a');
 +update PSlot set slotlink = 'PS.base.ta2' where slotname = 'PS.base.a3';
 +--
 +-- Install a hub at one of the patchfields, plug a computers
 +-- ethernet interface into the wall and patch it to the hub.
 +--
 +insert into Hub values ('base.hub1', 'Patchfield PF0_1 hub', 16);
 +insert into System values ('orion', 'PC');
 +insert into IFace values ('IF', 'orion', 'eth0', 'WS.002.1b');
 +update PSlot set slotlink = 'HS.base.hub1.1' where slotname = 'PS.base.b2';
 +--
 +-- Now we take a look at the patchfield
 +--
 +-- PGXCTODO: This is failing due to issue 3522907, complicated SELECT queries in plpgsql functions
 +select * from PField_v1 where pfname = 'PF0_1' order by slotname;
 + pfname |       slotname       |          backside          |      patch      
 +--------+----------------------+----------------------------+-----------------
 + PF0_1  | PS.base.a1           | WS.001.1a in room 001 -> - | PS.base.ta1 -> 
 + PF0_1  | PS.base.a2           |                            | -
 + PF0_1  | PS.base.a3           | WS.001.2a in room 001 -> - | PS.base.ta2 -> 
 + PF0_1  | PS.base.a4           | -                          | -
 + PF0_1  | PS.base.a5           | -                          | -
 + PF0_1  | PS.base.a6           | -                          | -
 + PF0_1  | PS.base.b1           |                            | PS.base.ta5 -> 
 + PF0_1  | PS.base.b2           |                            | 
 + PF0_1  | PS.base.b3           |                            | PS.base.tb2 -> 
 + PF0_1  | PS.base.b4           |                            | -
 + PF0_1  | PS.base.b5           | WS.002.3a in room 002 -> - | -
 + PF0_1  | PS.base.b6           |                            | -
 + PF0_1  | PS.base.c1           | WS.003.1a in room 003 -> - | -
 + PF0_1  | PS.base.c2           | WS.003.1b in room 003 -> - | -
 + PF0_1  | PS.base.c3           | WS.003.2a in room 003 -> - | -
 + PF0_1  | PS.base.c4           |                            | -
 + PF0_1  | PS.base.c5           | WS.003.3a in room 003 -> - | -
 + PF0_1  | PS.base.c6           |                            | -
 +(18 rows)
 +
 +select * from PField_v1 where pfname = 'PF0_2' order by slotname;
 + pfname | slotname | backside | patch 
 +--------+----------+----------+-------
 +(0 rows)
 +
 +--
 +-- Finally we want errors
 +--
 +insert into PField values ('PF1_1', 'should fail due to unique index');
 +ERROR:  duplicate key value violates unique constraint "pfield_name"
 +DETAIL:  Key (name)=(PF1_1) already exists.
 +update PSlot set backlink = 'WS.not.there' where slotname = 'PS.base.a1';
 +update PSlot set backlink = 'XX.illegal' where slotname = 'PS.base.a1';
 +update PSlot set slotlink = 'PS.not.there' where slotname = 'PS.base.a1';
 +update PSlot set slotlink = 'XX.illegal' where slotname = 'PS.base.a1';
 +insert into HSlot values ('HS', 'base.hub1', 1, '');
 +insert into HSlot values ('HS', 'base.hub1', 20, '');
 +ERROR:  duplicate key value violates unique constraint "hslot_name"
 +DETAIL:  Key (slotname)=(HS                  ) already exists.
 +delete from HSlot;
 +insert into IFace values ('IF', 'notthere', 'eth0', '');
 +ERROR:  duplicate key value violates unique constraint "iface_name"
 +DETAIL:  Key (slotname)=(IF                  ) already exists.
 +insert into IFace values ('IF', 'orion', 'ethernet_interface_name_too_long', '');
 +ERROR:  duplicate key value violates unique constraint "iface_name"
 +DETAIL:  Key (slotname)=(IF                  ) already exists.
 +--
 +-- The following tests are unrelated to the scenario outlined above;
 +-- they merely exercise specific parts of PL/pgSQL
 +--
 +--
 +-- Test recursion, per bug report 7-Sep-01
 +--
 +CREATE FUNCTION recursion_test(int,int) RETURNS text AS '
 +DECLARE rslt text;
 +BEGIN
 +    IF $1 <= 0 THEN
 +        rslt = CAST($2 AS TEXT);
 +    ELSE
 +        rslt = CAST($1 AS TEXT) || '','' || recursion_test($1 - 1, $2);
 +    END IF;
 +    RETURN rslt;
 +END;' LANGUAGE plpgsql;
 +SELECT recursion_test(4,3);
 + recursion_test 
 +----------------
 + 4,3,2,1,3
 +(1 row)
 +
 +--
 +-- Test the FOUND magic variable
 +--
 +CREATE TABLE found_test_tbl (a int) distribute by roundrobin;
 +create function test_found()
 +  returns boolean as '
 +  declare
 +  begin
 +  insert into found_test_tbl values (1);
 +  if FOUND then
 +     insert into found_test_tbl values (2);
 +  end if;
 +
 +  update found_test_tbl set a = 100 where a = 1;
 +  if FOUND then
 +    insert into found_test_tbl values (3);
 +  end if;
 +
 +  delete from found_test_tbl where a = 9999; -- matches no rows
 +  if not FOUND then
 +    insert into found_test_tbl values (4);
 +  end if;
 +
 +  for i in 1 .. 10 loop
 +    -- no need to do anything
 +  end loop;
 +  if FOUND then
 +    insert into found_test_tbl values (5);
 +  end if;
 +
 +  -- never executes the loop
 +  for i in 2 .. 1 loop
 +    -- no need to do anything
 +  end loop;
 +  if not FOUND then
 +    insert into found_test_tbl values (6);
 +  end if;
 +  return true;
 +  end;' language plpgsql;
 +select test_found();
 + test_found 
 +------------
 + t
 +(1 row)
 +
 +select * from found_test_tbl order by 1;
 +  a  
 +-----
 +   2
 +   3
 +   4
 +   5
 +   6
 + 100
 +(6 rows)
 +
 +--
 +-- Test set-returning functions for PL/pgSQL
 +--
 +create function test_table_func_rec() returns setof found_test_tbl as '
 +DECLARE
 +      rec RECORD;
 +BEGIN
 +      FOR rec IN select * from found_test_tbl LOOP
 +              RETURN NEXT rec;
 +      END LOOP;
 +      RETURN;
 +END;' language plpgsql;
 +select * from test_table_func_rec() order by 1;
 +  a  
 +-----
 +   2
 +   3
 +   4
 +   5
 +   6
 + 100
 +(6 rows)
 +
 +create function test_table_func_row() returns setof found_test_tbl as '
 +DECLARE
 +      row found_test_tbl%ROWTYPE;
 +BEGIN
 +      FOR row IN select * from found_test_tbl LOOP
 +              RETURN NEXT row;
 +      END LOOP;
 +      RETURN;
 +END;' language plpgsql;
 +select * from test_table_func_row() order by 1;
 +  a  
 +-----
 +   2
 +   3
 +   4
 +   5
 +   6
 + 100
 +(6 rows)
 +
 +create function test_ret_set_scalar(int,int) returns setof int as '
 +DECLARE
 +      i int;
 +BEGIN
 +      FOR i IN $1 .. $2 LOOP
 +              RETURN NEXT i + 1;
 +      END LOOP;
 +      RETURN;
 +END;' language plpgsql;
 +select * from test_ret_set_scalar(1,10) order by 1;
 + test_ret_set_scalar 
 +---------------------
 +                   2
 +                   3
 +                   4
 +                   5
 +                   6
 +                   7
 +                   8
 +                   9
 +                  10
 +                  11
 +(10 rows)
 +
 +create function test_ret_set_rec_dyn(int) returns setof record as '
 +DECLARE
 +      retval RECORD;
 +BEGIN
 +      IF $1 > 10 THEN
 +              SELECT INTO retval 5, 10, 15;
 +              RETURN NEXT retval;
 +              RETURN NEXT retval;
 +      ELSE
 +              SELECT INTO retval 50, 5::numeric, ''xxx''::text;
 +              RETURN NEXT retval;
 +              RETURN NEXT retval;
 +      END IF;
 +      RETURN;
 +END;' language plpgsql;
 +SELECT * FROM test_ret_set_rec_dyn(1500) AS (a int, b int, c int) order by a, b, c;
 + a | b  | c  
 +---+----+----
 + 5 | 10 | 15
 + 5 | 10 | 15
 +(2 rows)
 +
 +SELECT * FROM test_ret_set_rec_dyn(5) AS (a int, b numeric, c text) order by a, b, c;
 + a  | b |  c  
 +----+---+-----
 + 50 | 5 | xxx
 + 50 | 5 | xxx
 +(2 rows)
 +
 +create function test_ret_rec_dyn(int) returns record as '
 +DECLARE
 +      retval RECORD;
 +BEGIN
 +      IF $1 > 10 THEN
 +              SELECT INTO retval 5, 10, 15;
 +              RETURN retval;
 +      ELSE
 +              SELECT INTO retval 50, 5::numeric, ''xxx''::text;
 +              RETURN retval;
 +      END IF;
 +END;' language plpgsql;
 +SELECT * FROM test_ret_rec_dyn(1500) AS (a int, b int, c int) order by a, b, c;
 + a | b  | c  
 +---+----+----
 + 5 | 10 | 15
 +(1 row)
 +
 +SELECT * FROM test_ret_rec_dyn(5) AS (a int, b numeric, c text) order by a, b, c;
 + a  | b |  c  
 +----+---+-----
 + 50 | 5 | xxx
 +(1 row)
 +
 +--
 +-- Test handling of OUT parameters, including polymorphic cases.
 +-- Note that RETURN is optional with OUT params; we try both ways.
 +--
 +-- wrong way to do it:
 +create function f1(in i int, out j int) returns int as $$
 +begin
 +  return i+1;
 +end$$ language plpgsql;
 +ERROR:  RETURN cannot have a parameter in function with OUT parameters
 +LINE 3:   return i+1;
 +                 ^
 +create function f1(in i int, out j int) as $$
 +begin
 +  j := i+1;
 +  return;
 +end$$ language plpgsql;
 +select f1(42);
 + f1 
 +----
 + 43
 +(1 row)
 +
 +select * from f1(42);
 + j  
 +----
 + 43
 +(1 row)
 +
 +create or replace function f1(inout i int) as $$
 +begin
 +  i := i+1;
 +end$$ language plpgsql;
 +select f1(42);
 + f1 
 +----
 + 43
 +(1 row)
 +
 +select * from f1(42);
 + i  
 +----
 + 43
 +(1 row)
 +
 +drop function f1(int);
 +create function f1(in i int, out j int) returns setof int as $$
 +begin
 +  j := i+1;
 +  return next;
 +  j := i+2;
 +  return next;
 +  return;
 +end$$ language plpgsql;
 +select * from f1(42) order by 1;
 + j  
 +----
 + 43
 + 44
 +(2 rows)
 +
 +drop function f1(int);
 +create function f1(in i int, out j int, out k text) as $$
 +begin
 +  j := i;
 +  j := j+1;
 +  k := 'foo';
 +end$$ language plpgsql;
 +select f1(42);
 +    f1    
 +----------
 + (43,foo)
 +(1 row)
 +
 +select * from f1(42);
 + j  |  k  
 +----+-----
 + 43 | foo
 +(1 row)
 +
 +drop function f1(int);
 +create function f1(in i int, out j int, out k text) returns setof record as $$
 +begin
 +  j := i+1;
 +  k := 'foo';
 +  return next;
 +  j := j+1;
 +  k := 'foot';
 +  return next;
 +end$$ language plpgsql;
 +select * from f1(42) order by j, k;;
 + j  |  k   
 +----+------
 + 43 | foo
 + 44 | foot
 +(2 rows)
 +
 +drop function f1(int);
 +create function duplic(in i anyelement, out j anyelement, out k anyarray) as $$
 +begin
 +  j := i;
 +  k := array[j,j];
 +  return;
 +end$$ language plpgsql;
 +select * from duplic(42);
 + j  |    k    
 +----+---------
 + 42 | {42,42}
 +(1 row)
 +
 +select * from duplic('foo'::text);
 +  j  |     k     
 +-----+-----------
 + foo | {foo,foo}
 +(1 row)
 +
 +drop function duplic(anyelement);
 +--
 +-- test PERFORM
 +--
 +create table perform_test (
 +      a       INT,
 +      b       INT
 +);
 +create function simple_func(int) returns boolean as '
 +BEGIN
 +      IF $1 < 20 THEN
 +              INSERT INTO perform_test VALUES ($1, $1 + 10);
 +              RETURN TRUE;
 +      ELSE
 +              RETURN FALSE;
 +      END IF;
 +END;' language plpgsql;
 +create function perform_test_func() returns void as '
 +BEGIN
 +      IF FOUND then
 +              INSERT INTO perform_test VALUES (100, 100);
 +      END IF;
 +
 +      PERFORM simple_func(5);
 +
 +      IF FOUND then
 +              INSERT INTO perform_test VALUES (100, 100);
 +      END IF;
 +
 +      PERFORM simple_func(50);
 +
 +      IF FOUND then
 +              INSERT INTO perform_test VALUES (100, 100);
 +      END IF;
 +
 +      RETURN;
 +END;' language plpgsql;
 +SELECT perform_test_func();
 + perform_test_func 
 +-------------------
 + 
 +(1 row)
 +
 +SELECT * FROM perform_test order by a, b;
 +  a  |  b  
 +-----+-----
 +   5 |  15
 + 100 | 100
 + 100 | 100
 +(3 rows)
 +
 +drop table perform_test;
 +--
 +-- Test error trapping
 +--
 +create function trap_zero_divide(int) returns int as $$
 +declare x int;
 +      sx smallint;
 +begin
 +      begin   -- start a subtransaction
 +              raise notice 'should see this';
 +              x := 100 / $1;
 +              raise notice 'should see this only if % <> 0', $1;
 +              sx := $1;
 +              raise notice 'should see this only if % fits in smallint', $1;
 +              if $1 < 0 then
 +                      raise exception '% is less than zero', $1;
 +              end if;
 +      exception
 +              when division_by_zero then
 +                      raise notice 'caught division_by_zero';
 +                      x := -1;
 +              when NUMERIC_VALUE_OUT_OF_RANGE then
 +                      raise notice 'caught numeric_value_out_of_range';
 +                      x := -2;
 +      end;
 +      return x;
 +end$$ language plpgsql;
 +select trap_zero_divide(50);
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function trap_zero_divide(integer) line 5 during statement block entry
 +select trap_zero_divide(0);
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function trap_zero_divide(integer) line 5 during statement block entry
 +select trap_zero_divide(100000);
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function trap_zero_divide(integer) line 5 during statement block entry
 +select trap_zero_divide(-100);
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function trap_zero_divide(integer) line 5 during statement block entry
 +create function trap_matching_test(int) returns int as $$
 +declare x int;
 +      sx smallint;
 +      y int;
 +begin
 +      begin   -- start a subtransaction
 +              x := 100 / $1;
 +              sx := $1;
 +              select into y unique1 from tenk1 where unique2 =
 +                      (select unique2 from tenk1 b where ten = $1);
 +      exception
 +              when data_exception then  -- category match
 +                      raise notice 'caught data_exception';
 +                      x := -1;
 +              when NUMERIC_VALUE_OUT_OF_RANGE OR CARDINALITY_VIOLATION then
 +                      raise notice 'caught numeric_value_out_of_range or cardinality_violation';
 +                      x := -2;
 +      end;
 +      return x;
 +end$$ language plpgsql;
 +-- PGXCTODO: This is failing due to issue 3522907, complicated SELECT queries in plpgsql functions
 +select trap_matching_test(50);
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function trap_matching_test(integer) line 6 during statement block entry
 +select trap_matching_test(0);
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function trap_matching_test(integer) line 6 during statement block entry
 +select trap_matching_test(100000);
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function trap_matching_test(integer) line 6 during statement block entry
 +-- PGXCTODO: This is failing due to issue 3522907, complicated SELECT queries in plpgsql functions
 +select trap_matching_test(1);
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function trap_matching_test(integer) line 6 during statement block entry
 +create temp table foo (f1 int);
 +create function subxact_rollback_semantics() returns int as $$
 +declare x int;
 +begin
 +  x := 1;
 +  insert into foo values(x);
 +  begin
 +    x := x + 1;
 +    insert into foo values(x);
 +    raise exception 'inner';
 +  exception
 +    when others then
 +      x := x * 10;
 +  end;
 +  insert into foo values(x);
 +  return x;
 +end$$ language plpgsql;
 +select subxact_rollback_semantics();
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function subxact_rollback_semantics() line 6 during statement block entry
 +select * from foo;
 + f1 
 +----
 +  1
 +(1 row)
 +
 +drop table foo;
 +create function trap_timeout() returns void as $$
 +begin
 +  declare x int;
 +  begin
 +    -- we assume this will take longer than 2 seconds:
 +    select count(*) into x from tenk1 a, tenk1 b, tenk1 c;
 +  exception
 +    when others then
 +      raise notice 'caught others?';
 +    when query_canceled then
 +      raise notice 'nyeah nyeah, can''t stop me';
 +  end;
 +  -- Abort transaction to abandon the statement_timeout setting.  Otherwise,
 +  -- the next top-level statement would be vulnerable to the timeout.
 +  raise exception 'end of function';
 +end$$ language plpgsql;
 +begin;
 +set statement_timeout to 2000;
 +select trap_timeout();
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function trap_timeout() line 4 during statement block entry
 +rollback;
 +-- Test for pass-by-ref values being stored in proper context
 +create function test_variable_storage() returns text as $$
 +declare x text;
 +begin
 +  x := '1234';
 +  begin
 +    x := x || '5678';
 +    -- force error inside subtransaction SPI context
 +    perform trap_zero_divide(-100);
 +  exception
 +    when others then
 +      x := x || '9012';
 +  end;
 +  return x;
 +end$$ language plpgsql;
 +select test_variable_storage();
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function test_variable_storage() line 5 during statement block entry
 +--
 +-- test foreign key error trapping
 +--
 +create temp table master(f1 int primary key);
 +create temp table slave(f1 int references master deferrable);
 +insert into master values(1);
 +insert into slave values(1);
 +insert into slave values(2);  -- fails
 +ERROR:  insert or update on table "slave" violates foreign key constraint "slave_f1_fkey"
 +DETAIL:  Key (f1)=(2) is not present in table "master".
 +create function trap_foreign_key(int) returns int as $$
 +begin
 +      begin   -- start a subtransaction
 +              insert into slave values($1);
 +      exception
 +              when foreign_key_violation then
 +                      raise notice 'caught foreign_key_violation';
 +                      return 0;
 +      end;
 +      return 1;
 +end$$ language plpgsql;
 +create function trap_foreign_key_2() returns int as $$
 +begin
 +      begin   -- start a subtransaction
 +              set constraints all immediate;
 +      exception
 +              when foreign_key_violation then
 +                      raise notice 'caught foreign_key_violation';
 +                      return 0;
 +      end;
 +      return 1;
 +end$$ language plpgsql;
 +select trap_foreign_key(1);
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function trap_foreign_key(integer) line 3 during statement block entry
 +select trap_foreign_key(2);   -- detects FK violation
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function trap_foreign_key(integer) line 3 during statement block entry
 +begin;
 +  set constraints all deferred;
 +  select trap_foreign_key(2); -- should not detect FK violation
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function trap_foreign_key(integer) line 3 during statement block entry
 +  savepoint x;
 +ERROR:  current transaction is aborted, commands ignored until end of transaction block
 +    set constraints all immediate; -- fails
 +ERROR:  current transaction is aborted, commands ignored until end of transaction block
 +  rollback to x;
 +ERROR:  no such savepoint
 +  select trap_foreign_key_2();  -- detects FK violation
 +ERROR:  current transaction is aborted, commands ignored until end of transaction block
 +commit;                               -- still fails
 +drop function trap_foreign_key(int);
 +drop function trap_foreign_key_2();
 +--
 +-- Test proper snapshot handling in simple expressions
 +--
 +create temp table users(login text, id serial);
 +create function sp_id_user(a_login text) returns int as $$
 +declare x int;
 +begin
 +  select into x id from users where login = a_login;
 +  if found then return x; end if;
 +  return 0;
 +end$$ language plpgsql stable;
 +insert into users values('user1');
 +select sp_id_user('user1');
 + sp_id_user 
 +------------
 +          1
 +(1 row)
 +
 +select sp_id_user('userx');
 + sp_id_user 
 +------------
 +          0
 +(1 row)
 +
 +create function sp_add_user(a_login text) returns int as $$
 +declare my_id_user int;
 +begin
 +  my_id_user = sp_id_user( a_login );
 +  IF  my_id_user > 0 THEN
 +    RETURN -1;  -- error code for existing user
 +  END IF;
 +  INSERT INTO users ( login ) VALUES ( a_login );
 +  my_id_user = sp_id_user( a_login );
 +  IF  my_id_user = 0 THEN
 +    RETURN -2;  -- error code for insertion failure
 +  END IF;
 +  RETURN my_id_user;
 +end$$ language plpgsql;
 +select sp_add_user('user1');
 + sp_add_user 
 +-------------
 +          -1
 +(1 row)
 +
 +select sp_add_user('user2');
 + sp_add_user 
 +-------------
 +           2
 +(1 row)
 +
 +select sp_add_user('user2');
 + sp_add_user 
 +-------------
 +          -1
 +(1 row)
 +
 +select sp_add_user('user3');
 + sp_add_user 
 +-------------
 +           3
 +(1 row)
 +
 +select sp_add_user('user3');
 + sp_add_user 
 +-------------
 +          -1
 +(1 row)
 +
 +drop function sp_add_user(text);
 +drop function sp_id_user(text);
 +--
 +-- tests for refcursors
 +--
 +create table rc_test (a int, b int);
 +copy rc_test from stdin;
 +create function return_refcursor(rc refcursor) returns refcursor as $$
 +begin
 +    open rc for select a from rc_test;
 +    return rc;
 +end
 +$$ language plpgsql;
 +create function refcursor_test1(refcursor) returns refcursor as $$
 +begin
 +    perform return_refcursor($1);
 +    return $1;
 +end
 +$$ language plpgsql;
 +begin;
 +select refcursor_test1('test1');
 + refcursor_test1 
 +-----------------
 + test1
 +(1 row)
 +
 +fetch next in test1;
 + a 
 +---
 + 5
 +(1 row)
 +
 +select refcursor_test1('test2');
 + refcursor_test1 
 +-----------------
 + test2
 +(1 row)
 +
 +fetch all from test2;
 +  a  
 +-----
 +   5
 +  50
 + 500
 +(3 rows)
 +
 +commit;
 +-- should fail
 +fetch next from test1;
 +ERROR:  cursor "test1" does not exist
 +create function refcursor_test2(int, int) returns boolean as $$
 +declare
 +    c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2;
 +    nonsense record;
 +begin
 +    open c1($1, $2);
 +    fetch c1 into nonsense;
 +    close c1;
 +    if found then
 +        return true;
 +    else
 +        return false;
 +    end if;
 +end
 +$$ language plpgsql;
 +-- PGXCTODO: This is failing due to issue 3522907, complicated SELECT queries in plpgsql functions
 +select refcursor_test2(20000, 20000) as "Should be false",
 +       refcursor_test2(20, 20) as "Should be true";
 + Should be false | Should be true 
 +-----------------+----------------
 + f               | t
 +(1 row)
 +
 +--
 +-- tests for cursors with named parameter arguments
 +--
 +create function namedparmcursor_test1(int, int) returns boolean as $$
 +declare
 +    c1 cursor (param1 int, param12 int) for select * from rc_test where a > param1 and b > param12;
 +    nonsense record;
 +begin
 +    open c1(param12 := $2, param1 := $1);
 +    fetch c1 into nonsense;
 +    close c1;
 +    if found then
 +        return true;
 +    else
 +        return false;
 +    end if;
 +end
 +$$ language plpgsql;
 +select namedparmcursor_test1(20000, 20000) as "Should be false",
 +       namedparmcursor_test1(20, 20) as "Should be true";
 + Should be false | Should be true 
 +-----------------+----------------
 + f               | t
 +(1 row)
 +
 +-- mixing named and positional argument notations
 +create function namedparmcursor_test2(int, int) returns boolean as $$
 +declare
 +    c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2;
 +    nonsense record;
 +begin
 +    open c1(param1 := $1, $2);
 +    fetch c1 into nonsense;
 +    close c1;
 +    if found then
 +        return true;
 +    else
 +        return false;
 +    end if;
 +end
 +$$ language plpgsql;
 +select namedparmcursor_test2(20, 20);
 + namedparmcursor_test2 
 +-----------------------
 + t
 +(1 row)
 +
 +-- mixing named and positional: param2 is given twice, once in named notation
 +-- and second time in positional notation. Should throw an error at parse time
 +create function namedparmcursor_test3() returns void as $$
 +declare
 +    c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2;
 +begin
 +    open c1(param2 := 20, 21);
 +end
 +$$ language plpgsql;
 +ERROR:  value for parameter "param2" of cursor "c1" specified more than once
 +LINE 5:     open c1(param2 := 20, 21);
 +                                  ^
 +-- mixing named and positional: same as previous test, but param1 is duplicated
 +create function namedparmcursor_test4() returns void as $$
 +declare
 +    c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2;
 +begin
 +    open c1(20, param1 := 21);
 +end
 +$$ language plpgsql;
 +ERROR:  value for parameter "param1" of cursor "c1" specified more than once
 +LINE 5:     open c1(20, param1 := 21);
 +                        ^
 +-- duplicate named parameter, should throw an error at parse time
 +create function namedparmcursor_test5() returns void as $$
 +declare
 +  c1 cursor (p1 int, p2 int) for
 +    select * from tenk1 where thousand = p1 and tenthous = p2;
 +begin
 +  open c1 (p2 := 77, p2 := 42);
 +end
 +$$ language plpgsql;
 +ERROR:  value for parameter "p2" of cursor "c1" specified more than once
 +LINE 6:   open c1 (p2 := 77, p2 := 42);
 +                             ^
 +-- not enough parameters, should throw an error at parse time
 +create function namedparmcursor_test6() returns void as $$
 +declare
 +  c1 cursor (p1 int, p2 int) for
 +    select * from tenk1 where thousand = p1 and tenthous = p2;
 +begin
 +  open c1 (p2 := 77);
 +end
 +$$ language plpgsql;
 +ERROR:  not enough arguments for cursor "c1"
 +LINE 6:   open c1 (p2 := 77);
 +                           ^
 +-- division by zero runtime error, the context given in the error message
 +-- should be sensible
 +create function namedparmcursor_test7() returns void as $$
 +declare
 +  c1 cursor (p1 int, p2 int) for
 +    select * from tenk1 where thousand = p1 and tenthous = p2;
 +begin
 +  open c1 (p2 := 77, p1 := 42/0);
 +end $$ language plpgsql;
 +select namedparmcursor_test7();
 +ERROR:  division by zero
 +CONTEXT:  SQL statement "SELECT 42/0 AS p1, 77 AS p2;"
 +PL/pgSQL function namedparmcursor_test7() line 6 at OPEN
 +-- check that line comments work correctly within the argument list (there
 +-- is some special handling of this case in the code: the newline after the
 +-- comment must be preserved when the argument-evaluating query is
 +-- constructed, otherwise the comment effectively comments out the next
 +-- argument, too)
 +create function namedparmcursor_test8() returns int4 as $$
 +declare
 +  c1 cursor (p1 int, p2 int) for
 +    select count(*) from tenk1 where thousand = p1 and tenthous = p2;
 +  n int4;
 +begin
 +  open c1 (77 -- test
 +  , 42);
 +  fetch c1 into n;
 +  return n;
 +end $$ language plpgsql;
 +select namedparmcursor_test8();
 + namedparmcursor_test8 
 +-----------------------
 +                     0
 +(1 row)
 +
 +-- cursor parameter name can match plpgsql variable or unreserved keyword
 +create function namedparmcursor_test9(p1 int) returns int4 as $$
 +declare
 +  c1 cursor (p1 int, p2 int, debug int) for
 +    select count(*) from tenk1 where thousand = p1 and tenthous = p2
 +      and four = debug;
 +  p2 int4 := 1006;
 +  n int4;
 +begin
 +  open c1 (p1 := p1, p2 := p2, debug := 2);
 +  fetch c1 into n;
 +  return n;
 +end $$ language plpgsql;
 +select namedparmcursor_test9(6);
 + namedparmcursor_test9 
 +-----------------------
 +                     1
 +(1 row)
 +
 +--
 +-- tests for "raise" processing
 +--
 +create function raise_test1(int) returns int as $$
 +begin
 +    raise notice 'This message has too many parameters!', $1;
 +    return $1;
 +end;
 +$$ language plpgsql;
 +ERROR:  too many parameters specified for RAISE
 +CONTEXT:  compilation of PL/pgSQL function "raise_test1" near line 3
 +create function raise_test2(int) returns int as $$
 +begin
 +    raise notice 'This message has too few parameters: %, %, %', $1, $1;
 +    return $1;
 +end;
 +$$ language plpgsql;
 +ERROR:  too few parameters specified for RAISE
 +CONTEXT:  compilation of PL/pgSQL function "raise_test2" near line 3
 +create function raise_test3(int) returns int as $$
 +begin
 +    raise notice 'This message has no parameters (despite having %% signs in it)!';
 +    return $1;
 +end;
 +$$ language plpgsql;
 +select raise_test3(1);
 +NOTICE:  This message has no parameters (despite having % signs in it)!
 + raise_test3 
 +-------------
 +           1
 +(1 row)
 +
 +-- Test re-RAISE inside a nested exception block.  This case is allowed
 +-- by Oracle's PL/SQL but was handled differently by PG before 9.1.
 +CREATE FUNCTION reraise_test() RETURNS void AS $$
 +BEGIN
 +   BEGIN
 +       RAISE syntax_error;
 +   EXCEPTION
 +       WHEN syntax_error THEN
 +           BEGIN
 +               raise notice 'exception % thrown in inner block, reraising', sqlerrm;
 +               RAISE;
 +           EXCEPTION
 +               WHEN OTHERS THEN
 +                   raise notice 'RIGHT - exception % caught in inner block', sqlerrm;
 +           END;
 +   END;
 +EXCEPTION
 +   WHEN OTHERS THEN
 +       raise notice 'WRONG - exception % caught in outer block', sqlerrm;
 +END;
 +$$ LANGUAGE plpgsql;
 +SELECT reraise_test();
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function reraise_test() line 2 during statement block entry
 +--
 +-- reject function definitions that contain malformed SQL queries at
 +-- compile-time, where possible
 +--
 +create function bad_sql1() returns int as $$
 +declare a int;
 +begin
 +    a := 5;
 +    Johnny Yuma;
 +    a := 10;
 +    return a;
 +end$$ language plpgsql;
 +ERROR:  syntax error at or near "Johnny"
 +LINE 5:     Johnny Yuma;
 +            ^
 +create function bad_sql2() returns int as $$
 +declare r record;
 +begin
 +    for r in select I fought the law, the law won LOOP
 +        raise notice 'in loop';
 +    end loop;
 +    return 5;
 +end;$$ language plpgsql;
 +ERROR:  syntax error at or near "the"
 +LINE 4:     for r in select I fought the law, the law won LOOP
 +                                     ^
 +-- a RETURN expression is mandatory, except for void-returning
 +-- functions, where it is not allowed
 +create function missing_return_expr() returns int as $$
 +begin
 +    return ;
 +end;$$ language plpgsql;
 +ERROR:  missing expression at or near ";"
 +LINE 3:     return ;
 +                   ^
 +create function void_return_expr() returns void as $$
 +begin
 +    return 5;
 +end;$$ language plpgsql;
 +ERROR:  RETURN cannot have a parameter in function returning void
 +LINE 3:     return 5;
 +                   ^
 +-- VOID functions are allowed to omit RETURN
 +create function void_return_expr() returns void as $$
 +begin
 +    perform 2+2;
 +end;$$ language plpgsql;
 +select void_return_expr();
 + void_return_expr 
 +------------------
 + 
 +(1 row)
 +
 +-- but ordinary functions are not
 +create function missing_return_expr() returns int as $$
 +begin
 +    perform 2+2;
 +end;$$ language plpgsql;
 +select missing_return_expr();
 +ERROR:  control reached end of function without RETURN
 +CONTEXT:  PL/pgSQL function missing_return_expr()
 +drop function void_return_expr();
 +drop function missing_return_expr();
 +--
 +-- EXECUTE ... INTO test
 +--
 +create table eifoo (i integer, y integer);
 +create type eitype as (i integer, y integer);
 +create or replace function execute_into_test(varchar) returns record as $$
 +declare
 +    _r record;
 +    _rt eifoo%rowtype;
 +    _v eitype;
 +    i int;
 +    j int;
 +    k int;
 +begin
 +    execute 'insert into '||$1||' values(10,15)';
 +    execute 'select (row).* from (select row(10,1)::eifoo) s' into _r;
 +    raise notice '% %', _r.i, _r.y;
 +    execute 'select * from '||$1||' limit 1' into _rt;
 +    raise notice '% %', _rt.i, _rt.y;
 +    execute 'select *, 20 from '||$1||' limit 1' into i, j, k;
 +    raise notice '% % %', i, j, k;
 +    execute 'select 1,2' into _v;
 +    return _v;
 +end; $$ language plpgsql;
 +select execute_into_test('eifoo');
 +NOTICE:  10 1
 +NOTICE:  <NULL> <NULL>
 +NOTICE:  <NULL> <NULL> <NULL>
 + execute_into_test 
 +-------------------
 + (1,2)
 +(1 row)
 +
 +drop table eifoo cascade;
 +drop type eitype cascade;
 +--
 +-- SQLSTATE and SQLERRM test
 +--
 +create function excpt_test1() returns void as $$
 +begin
 +    raise notice '% %', sqlstate, sqlerrm;
 +end; $$ language plpgsql;
 +-- should fail: SQLSTATE and SQLERRM are only in defined EXCEPTION
 +-- blocks
 +select excpt_test1();
 +ERROR:  column "sqlstate" does not exist
 +LINE 1: SELECT sqlstate
 +               ^
 +QUERY:  SELECT sqlstate
 +CONTEXT:  PL/pgSQL function excpt_test1() line 3 at RAISE
 +create function excpt_test2() returns void as $$
 +begin
 +    begin
 +        begin
 +            raise notice '% %', sqlstate, sqlerrm;
 +        end;
 +    end;
 +end; $$ language plpgsql;
 +-- should fail
 +select excpt_test2();
 +ERROR:  column "sqlstate" does not exist
 +LINE 1: SELECT sqlstate
 +               ^
 +QUERY:  SELECT sqlstate
 +CONTEXT:  PL/pgSQL function excpt_test2() line 5 at RAISE
 +create function excpt_test3() returns void as $$
 +begin
 +    begin
 +        raise exception 'user exception';
 +    exception when others then
 +          raise notice 'caught exception % %', sqlstate, sqlerrm;
 +          begin
 +              raise notice '% %', sqlstate, sqlerrm;
 +              perform 10/0;
 +        exception
 +            when substring_error then
 +                -- this exception handler shouldn't be invoked
 +                raise notice 'unexpected exception: % %', sqlstate, sqlerrm;
 +              when division_by_zero then
 +                  raise notice 'caught exception % %', sqlstate, sqlerrm;
 +          end;
 +          raise notice '% %', sqlstate, sqlerrm;
 +    end;
 +end; $$ language plpgsql;
 +select excpt_test3();
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function excpt_test3() line 3 during statement block entry
 +create function excpt_test4() returns text as $$
 +begin
 +      begin perform 1/0;
 +      exception when others then return sqlerrm; end;
 +end; $$ language plpgsql;
 +select excpt_test4();
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function excpt_test4() line 3 during statement block entry
 +drop function excpt_test1();
 +drop function excpt_test2();
 +drop function excpt_test3();
 +drop function excpt_test4();
 +-- parameters of raise stmt can be expressions
 +create function raise_exprs() returns void as $$
 +declare
 +    a integer[] = '{10,20,30}';
 +    c varchar = 'xyz';
 +    i integer;
 +begin
 +    i := 2;
 +    raise notice '%; %; %; %; %; %', a, a[i], c, (select c || 'abc'), row(10,'aaa',NULL,30), NULL;
 +end;$$ language plpgsql;
 +select raise_exprs();
 +NOTICE:  {10,20,30}; 20; xyz; xyzabc; (10,aaa,,30); <NULL>
 + raise_exprs 
 +-------------
 + 
 +(1 row)
 +
 +drop function raise_exprs();
 +-- continue statement
 +create table conttesttbl(idx serial, v integer);
 +insert into conttesttbl(v) values(10);
 +insert into conttesttbl(v) values(20);
 +insert into conttesttbl(v) values(30);
 +insert into conttesttbl(v) values(40);
 +create function continue_test1() returns void as $$
 +declare _i integer = 0; _r record;
 +begin
 +  raise notice '---1---';
 +  loop
 +    _i := _i + 1;
 +    raise notice '%', _i;
 +    continue when _i < 10;
 +    exit;
 +  end loop;
 +
 +  raise notice '---2---';
 +  <<lbl>>
 +  loop
 +    _i := _i - 1;
 +    loop
 +      raise notice '%', _i;
 +      continue lbl when _i > 0;
 +      exit lbl;
 +    end loop;
 +  end loop;
 +
 +  raise notice '---3---';
 +  <<the_loop>>
 +  while _i < 10 loop
 +    _i := _i + 1;
 +    continue the_loop when _i % 2 = 0;
 +    raise notice '%', _i;
 +  end loop;
 +
 +  raise notice '---4---';
 +  for _i in 1..10 loop
 +    begin
 +      -- applies to outer loop, not the nested begin block
 +      continue when _i < 5;
 +      raise notice '%', _i;
 +    end;
 +  end loop;
 +
 +  raise notice '---5---';
 +  for _r in select * from conttesttbl loop
 +    continue when _r.v <= 20;
 +    raise notice '%', _r.v;
 +  end loop;
 +
 +  raise notice '---6---';
 +  for _r in execute 'select * from conttesttbl' loop
 +    continue when _r.v <= 20;
 +    raise notice '%', _r.v;
 +  end loop;
 +
 +  raise notice '---7---';
 +  for _i in 1..3 loop
 +    raise notice '%', _i;
 +    continue when _i = 3;
 +  end loop;
 +
 +  raise notice '---8---';
 +  _i := 1;
 +  while _i <= 3 loop
 +    raise notice '%', _i;
 +    _i := _i + 1;
 +    continue when _i = 3;
 +  end loop;
 +
 +  raise notice '---9---';
 +  for _r in select * from conttesttbl order by v limit 1 loop
 +    raise notice '%', _r.v;
 +    continue;
 +  end loop;
 +
 +  raise notice '---10---';
 +  for _r in execute 'select * from conttesttbl order by v limit 1' loop
 +    raise notice '%', _r.v;
 +    continue;
 +  end loop;
 +end; $$ language plpgsql;
 +select continue_test1();
 +NOTICE:  ---1---
 +NOTICE:  1
 +NOTICE:  2
 +NOTICE:  3
 +NOTICE:  4
 +NOTICE:  5
 +NOTICE:  6
 +NOTICE:  7
 +NOTICE:  8
 +NOTICE:  9
 +NOTICE:  10
 +NOTICE:  ---2---
 +NOTICE:  9
 +NOTICE:  8
 +NOTICE:  7
 +NOTICE:  6
 +NOTICE:  5
 +NOTICE:  4
 +NOTICE:  3
 +NOTICE:  2
 +NOTICE:  1
 +NOTICE:  0
 +NOTICE:  ---3---
 +NOTICE:  1
 +NOTICE:  3
 +NOTICE:  5
 +NOTICE:  7
 +NOTICE:  9
 +NOTICE:  ---4---
 +NOTICE:  5
 +NOTICE:  6
 +NOTICE:  7
 +NOTICE:  8
 +NOTICE:  9
 +NOTICE:  10
 +NOTICE:  ---5---
 +NOTICE:  30
 +NOTICE:  40
 +NOTICE:  ---6---
 +NOTICE:  30
 +NOTICE:  40
 +NOTICE:  ---7---
 +NOTICE:  1
 +NOTICE:  2
 +NOTICE:  3
 +NOTICE:  ---8---
 +NOTICE:  1
 +NOTICE:  2
 +NOTICE:  3
 +NOTICE:  ---9---
 +NOTICE:  10
 +NOTICE:  ---10---
 +NOTICE:  10
 + continue_test1 
 +----------------
 + 
 +(1 row)
 +
 +-- CONTINUE is only legal inside a loop
 +create function continue_test2() returns void as $$
 +begin
 +    begin
 +        continue;
 +    end;
 +    return;
 +end;
 +$$ language plpgsql;
 +-- should fail
 +select continue_test2();
 +ERROR:  CONTINUE cannot be used outside a loop
 +CONTEXT:  PL/pgSQL function continue_test2()
 +-- CONTINUE can't reference the label of a named block
 +create function continue_test3() returns void as $$
 +begin
 +    <<begin_block1>>
 +    begin
 +        loop
 +            continue begin_block1;
 +        end loop;
 +    end;
 +end;
 +$$ language plpgsql;
 +-- should fail
 +select continue_test3();
 +ERROR:  CONTINUE cannot be used outside a loop
 +CONTEXT:  PL/pgSQL function continue_test3()
 +drop function continue_test1();
 +drop function continue_test2();
 +drop function continue_test3();
 +drop table conttesttbl;
 +-- verbose end block and end loop
 +create function end_label1() returns void as $$
 +<<blbl>>
 +begin
 +  <<flbl1>>
 +  for _i in 1 .. 10 loop
 +    exit flbl1;
 +  end loop flbl1;
 +  <<flbl2>>
 +  for _i in 1 .. 10 loop
 +    exit flbl2;
 +  end loop;
 +end blbl;
 +$$ language plpgsql;
 +select end_label1();
 + end_label1 
 +------------
 + 
 +(1 row)
 +
 +drop function end_label1();
 +-- should fail: undefined end label
 +create function end_label2() returns void as $$
 +begin
 +  for _i in 1 .. 10 loop
 +    exit;
 +  end loop flbl1;
 +end;
 +$$ language plpgsql;
 +ERROR:  label does not exist at or near "flbl1"
 +LINE 5:   end loop flbl1;
 +                   ^
 +-- should fail: end label does not match start label
 +create function end_label3() returns void as $$
 +<<outer_label>>
 +begin
 +  <<inner_label>>
 +  for _i in 1 .. 10 loop
 +    exit;
 +  end loop outer_label;
 +end;
 +$$ language plpgsql;
 +ERROR:  end label "outer_label" differs from block's label "inner_label"
 +LINE 7:   end loop outer_label;
 +                   ^
 +-- should fail: end label on a block without a start label
 +create function end_label4() returns void as $$
 +<<outer_label>>
 +begin
 +  for _i in 1 .. 10 loop
 +    exit;
 +  end loop outer_label;
 +end;
 +$$ language plpgsql;
 +ERROR:  end label "outer_label" specified for unlabelled block
 +LINE 6:   end loop outer_label;
 +                   ^
 +-- using list of scalars in fori and fore stmts
 +create function for_vect() returns void as $proc$
 +<<lbl>>declare a integer; b varchar; c varchar; r record;
 +begin
 +  -- fori
 +  for i in 1 .. 3 loop
 +    raise notice '%', i;
 +  end loop;
 +  -- fore with record var
 +  for r in select gs as aa, 'BB' as bb, 'CC' as cc from generate_series(1,4) gs loop
 +    raise notice '% % %', r.aa, r.bb, r.cc;
 +  end loop;
 +  -- fore with single scalar
 +  for a in select gs from generate_series(1,4) gs loop
 +    raise notice '%', a;
 +  end loop;
 +  -- fore with multiple scalars
 +  for a,b,c in select gs, 'BB','CC' from generate_series(1,4) gs loop
 +    raise notice '% % %', a, b, c;
 +  end loop;
 +  -- using qualified names in fors, fore is enabled, disabled only for fori
 +  for lbl.a, lbl.b, lbl.c in execute $$select gs, 'bb','cc' from generate_series(1,4) gs$$ loop
 +    raise notice '% % %', a, b, c;
 +  end loop;
 +end;
 +$proc$ language plpgsql;
 +select for_vect();
 +NOTICE:  1
 +NOTICE:  2
 +NOTICE:  3
 +NOTICE:  1 BB CC
 +NOTICE:  2 BB CC
 +NOTICE:  3 BB CC
 +NOTICE:  4 BB CC
 +NOTICE:  1
 +NOTICE:  2
 +NOTICE:  3
 +NOTICE:  4
 +NOTICE:  1 BB CC
 +NOTICE:  2 BB CC
 +NOTICE:  3 BB CC
 +NOTICE:  4 BB CC
 +NOTICE:  1 bb cc
 +NOTICE:  2 bb cc
 +NOTICE:  3 bb cc
 +NOTICE:  4 bb cc
 + for_vect 
 +----------
 + 
 +(1 row)
 +
 +-- regression test: verify that multiple uses of same plpgsql datum within
 +-- a SQL command all get mapped to the same $n parameter.  The return value
 +-- of the SELECT is not important, we only care that it doesn't fail with
 +-- a complaint about an ungrouped column reference.
 +create function multi_datum_use(p1 int) returns bool as $$
 +declare
 +  x int;
 +  y int;
 +begin
 +  select into x,y unique1/p1, unique1/$1 from tenk1 group by unique1/p1;
 +  return x = y;
 +end$$ language plpgsql;
 +select multi_datum_use(42);
 + multi_datum_use 
 +-----------------
 + t
 +(1 row)
 +
 +--
 +-- Test STRICT limiter in both planned and EXECUTE invocations.
 +-- Note that a data-modifying query is quasi strict (disallow multi rows)
 +-- by default in the planned case, but not in EXECUTE.
 +--
 +create temp table foo (f1 int, f2 int);
 +insert into foo values (1,2), (3,4);
 +create or replace function footest() returns void as $$
 +declare x record;
 +begin
 +  -- should work
 +  insert into foo values(5,6) returning * into x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +NOTICE:  x.f1 = 5, x.f2 = 6
 + footest 
 +---------
 + 
 +(1 row)
 +
 +create or replace function footest() returns void as $$
 +declare x record;
 +begin
 +  -- should fail due to implicit strict
 +  insert into foo values(7,8),(9,10) returning * into x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +ERROR:  consistency check on SPI tuple count failed
 +CONTEXT:  SQL statement "insert into foo values(7,8),(9,10) returning *"
 +PL/pgSQL function footest() line 5 at SQL statement
 +create or replace function footest() returns void as $$
 +declare x record;
 +begin
 +  -- should work
 +  execute 'insert into foo values(5,6) returning *' into x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +NOTICE:  x.f1 = 5, x.f2 = 6
 + footest 
 +---------
 + 
 +(1 row)
 +
 +create or replace function footest() returns void as $$
 +declare x record;
 +begin
 +  -- this should work since EXECUTE isn't as picky
 +  execute 'insert into foo values(7,8),(9,10) returning *' into x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +NOTICE:  x.f1 = 9, x.f2 = 10
 + footest 
 +---------
 + 
 +(1 row)
 +
 +select * from foo order by 1, 2;
 + f1 | f2 
 +----+----
 +  1 |  2
 +  3 |  4
 +  5 |  6
 +  5 |  6
 +  7 |  8
 +  9 | 10
 +(6 rows)
 +
 +create or replace function footest() returns void as $$
 +declare x record;
 +begin
 +  -- should work
 +  select * from foo where f1 = 3 into strict x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +NOTICE:  x.f1 = 3, x.f2 = 4
 + footest 
 +---------
 + 
 +(1 row)
 +
 +create or replace function footest() returns void as $$
 +declare x record;
 +begin
 +  -- should fail, no rows
 +  select * from foo where f1 = 0 into strict x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +ERROR:  query returned no rows
 +CONTEXT:  PL/pgSQL function footest() line 5 at SQL statement
 +create or replace function footest() returns void as $$
 +declare x record;
 +begin
 +  -- should fail, too many rows
 +  select * from foo where f1 > 3 into strict x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +ERROR:  query returned more than one row
 +CONTEXT:  PL/pgSQL function footest() line 5 at SQL statement
 +create or replace function footest() returns void as $$
 +declare x record;
 +begin
 +  -- should work
 +  execute 'select * from foo where f1 = 3' into strict x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +NOTICE:  x.f1 = 3, x.f2 = 4
 + footest 
 +---------
 + 
 +(1 row)
 +
 +create or replace function footest() returns void as $$
 +declare x record;
 +begin
 +  -- should fail, no rows
 +  execute 'select * from foo where f1 = 0' into strict x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +ERROR:  query returned no rows
 +CONTEXT:  PL/pgSQL function footest() line 5 at EXECUTE
 +create or replace function footest() returns void as $$
 +declare x record;
 +begin
 +  -- should fail, too many rows
 +  execute 'select * from foo where f1 > 3' into strict x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +ERROR:  query returned more than one row
 +CONTEXT:  PL/pgSQL function footest() line 5 at EXECUTE
 +drop function footest();
 +-- test printing parameters after failure due to STRICT
 +set plpgsql.print_strict_params to true;
 +create or replace function footest() returns void as $$
 +declare
 +x record;
 +p1 int := 2;
 +p3 text := 'foo';
 +begin
 +  -- no rows
 +  select * from foo where f1 = p1 and f1::text = p3 into strict x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +ERROR:  query returned no rows
 +DETAIL:  parameters: p1 = '2', p3 = 'foo'
 +CONTEXT:  PL/pgSQL function footest() line 8 at SQL statement
 +create or replace function footest() returns void as $$
 +declare
 +x record;
 +p1 int := 2;
 +p3 text := 'foo';
 +begin
 +  -- too many rows
 +  select * from foo where f1 > p1 or f1::text = p3  into strict x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +ERROR:  query returned more than one row
 +DETAIL:  parameters: p1 = '2', p3 = 'foo'
 +CONTEXT:  PL/pgSQL function footest() line 8 at SQL statement
 +create or replace function footest() returns void as $$
 +declare x record;
 +begin
 +  -- too many rows, no params
 +  select * from foo where f1 > 3 into strict x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +ERROR:  query returned more than one row
 +CONTEXT:  PL/pgSQL function footest() line 5 at SQL statement
 +create or replace function footest() returns void as $$
 +declare x record;
 +begin
 +  -- no rows
 +  execute 'select * from foo where f1 = $1 or f1::text = $2' using 0, 'foo' into strict x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +ERROR:  query returned no rows
 +DETAIL:  parameters: $1 = '0', $2 = 'foo'
 +CONTEXT:  PL/pgSQL function footest() line 5 at EXECUTE
 +create or replace function footest() returns void as $$
 +declare x record;
 +begin
 +  -- too many rows
 +  execute 'select * from foo where f1 > $1' using 1 into strict x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +ERROR:  query returned more than one row
 +DETAIL:  parameters: $1 = '1'
 +CONTEXT:  PL/pgSQL function footest() line 5 at EXECUTE
 +create or replace function footest() returns void as $$
 +declare x record;
 +begin
 +  -- too many rows, no parameters
 +  execute 'select * from foo where f1 > 3' into strict x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +ERROR:  query returned more than one row
 +CONTEXT:  PL/pgSQL function footest() line 5 at EXECUTE
 +create or replace function footest() returns void as $$
 +-- override the global
 +#print_strict_params off
 +declare
 +x record;
 +p1 int := 2;
 +p3 text := 'foo';
 +begin
 +  -- too many rows
 +  select * from foo where f1 > p1 or f1::text = p3  into strict x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +ERROR:  query returned more than one row
 +CONTEXT:  PL/pgSQL function footest() line 10 at SQL statement
 +reset plpgsql.print_strict_params;
 +create or replace function footest() returns void as $$
 +-- override the global
 +#print_strict_params on
 +declare
 +x record;
 +p1 int := 2;
 +p3 text := 'foo';
 +begin
 +  -- too many rows
 +  select * from foo where f1 > p1 or f1::text = p3  into strict x;
 +  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 +end$$ language plpgsql;
 +select footest();
 +ERROR:  query returned more than one row
 +DETAIL:  parameters: p1 = '2', p3 = 'foo'
 +CONTEXT:  PL/pgSQL function footest() line 10 at SQL statement
 +-- test warnings and errors
 +set plpgsql.extra_warnings to 'all';
 +ERROR:  syntax error at or near "all"
 +set plpgsql.extra_warnings to 'none';
 +set plpgsql.extra_errors to 'all';
 +ERROR:  syntax error at or near "all"
 +set plpgsql.extra_errors to 'none';
 +-- test warnings when shadowing a variable
 +set plpgsql.extra_warnings to 'shadowed_variables';
 +-- simple shadowing of input and output parameters
 +create or replace function shadowtest(in1 int)
 +      returns table (out1 int) as $$
 +declare
 +in1 int;
 +out1 int;
 +begin
 +end
 +$$ language plpgsql;
 +WARNING:  variable "in1" shadows a previously defined variable
 +LINE 4: in1 int;
 +        ^
 +WARNING:  variable "out1" shadows a previously defined variable
 +LINE 5: out1 int;
 +        ^
 +select shadowtest(1);
 + shadowtest 
 +------------
 +(0 rows)
 +
 +set plpgsql.extra_warnings to 'shadowed_variables';
 +select shadowtest(1);
 + shadowtest 
 +------------
 +(0 rows)
 +
 +create or replace function shadowtest(in1 int)
 +      returns table (out1 int) as $$
 +declare
 +in1 int;
 +out1 int;
 +begin
 +end
 +$$ language plpgsql;
 +WARNING:  variable "in1" shadows a previously defined variable
 +LINE 4: in1 int;
 +        ^
 +WARNING:  variable "out1" shadows a previously defined variable
 +LINE 5: out1 int;
 +        ^
 +select shadowtest(1);
 + shadowtest 
 +------------
 +(0 rows)
 +
 +drop function shadowtest(int);
 +-- shadowing in a second DECLARE block
 +create or replace function shadowtest()
 +      returns void as $$
 +declare
 +f1 int;
 +begin
 +      declare
 +      f1 int;
 +      begin
 +      end;
 +end$$ language plpgsql;
 +WARNING:  variable "f1" shadows a previously defined variable
 +LINE 7:  f1 int;
 +         ^
 +drop function shadowtest();
 +-- several levels of shadowing
 +create or replace function shadowtest(in1 int)
 +      returns void as $$
 +declare
 +in1 int;
 +begin
 +      declare
 +      in1 int;
 +      begin
 +      end;
 +end$$ language plpgsql;
 +WARNING:  variable "in1" shadows a previously defined variable
 +LINE 4: in1 int;
 +        ^
 +WARNING:  variable "in1" shadows a previously defined variable
 +LINE 7:  in1 int;
 +         ^
 +drop function shadowtest(int);
 +-- shadowing in cursor definitions
 +create or replace function shadowtest()
 +      returns void as $$
 +declare
 +f1 int;
 +c1 cursor (f1 int) for select 1;
 +begin
 +end$$ language plpgsql;
 +WARNING:  variable "f1" shadows a previously defined variable
 +LINE 5: c1 cursor (f1 int) for select 1;
 +                   ^
 +drop function shadowtest();
 +-- test errors when shadowing a variable
 +set plpgsql.extra_errors to 'shadowed_variables';
 +create or replace function shadowtest(f1 int)
 +      returns boolean as $$
 +declare f1 int; begin return 1; end $$ language plpgsql;
 +ERROR:  variable "f1" shadows a previously defined variable
 +LINE 3: declare f1 int; begin return 1; end $$ language plpgsql;
 +                ^
 +select shadowtest(1);
 +ERROR:  function shadowtest(integer) does not exist
 +LINE 1: select shadowtest(1);
 +               ^
 +HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 +reset plpgsql.extra_errors;
 +reset plpgsql.extra_warnings;
 +create or replace function shadowtest(f1 int)
 +      returns boolean as $$
 +declare f1 int; begin return 1; end $$ language plpgsql;
 +select shadowtest(1);
 + shadowtest 
 +------------
 + t
 +(1 row)
 +
 +-- test scrollable cursor support
 +create function sc_test() returns setof integer as $$
 +declare
 +  c scroll cursor for select f1 from int4_tbl;
 +  x integer;
 +begin
 +  open c;
 +  fetch last from c into x;
 +  while found loop
 +    return next x;
 +    fetch prior from c into x;
 +  end loop;
 +  close c;
 +end;
 +$$ language plpgsql;
 +select * from sc_test() order by 1;
 + sc_test 
 +---------
 +(0 rows)
 +
 +create or replace function sc_test() returns setof integer as $$
 +declare
 +  c no scroll cursor for select f1 from int4_tbl;
 +  x integer;
 +begin
 +  open c;
 +  fetch last from c into x;
 +  while found loop
 +    return next x;
 +    fetch prior from c into x;
 +  end loop;
 +  close c;
 +end;
 +$$ language plpgsql;
 +select * from sc_test() order by 1;  -- fails because of NO SCROLL specification
 +ERROR:  cursor can only scan forward
 +HINT:  Declare it with SCROLL option to enable backward scan.
 +CONTEXT:  PL/pgSQL function sc_test() line 7 at FETCH
 +create or replace function sc_test() returns setof integer as $$
 +declare
 +  c refcursor;
 +  x integer;
 +begin
 +  open c scroll for select f1 from int4_tbl;
 +  fetch last from c into x;
 +  while found loop
 +    return next x;
 +    fetch prior from c into x;
 +  end loop;
 +  close c;
 +end;
 +$$ language plpgsql;
 +select * from sc_test() order by 1;
 + sc_test 
 +---------
 +(0 rows)
 +
 +create or replace function sc_test() returns setof integer as $$
 +declare
 +  c refcursor;
 +  x integer;
 +begin
 +  open c scroll for execute 'select f1 from int4_tbl order by 1';
 +  fetch last from c into x;
 +  while found loop
 +    return next x;
 +    fetch relative -2 from c into x;
 +  end loop;
 +  close c;
 +end;
 +$$ language plpgsql;
 +select * from sc_test();
 + sc_test 
 +---------
 +(0 rows)
 +
 +create or replace function sc_test() returns setof integer as $$
 +declare
 +  c refcursor;
 +  x integer;
 +begin
 +  open c scroll for execute 'select f1 from int4_tbl order by 1';
 +  fetch last from c into x;
 +  while found loop
 +    return next x;
 +    move backward 2 from c;
 +    fetch relative -1 from c into x;
 +  end loop;
 +  close c;
 +end;
 +$$ language plpgsql;
 +select * from sc_test();
 + sc_test 
 +---------
 +(0 rows)
 +
 +create or replace function sc_test() returns setof integer as $$
 +declare
 +  c cursor for select * from generate_series(1, 10);
 +  x integer;
 +begin
 +  open c;
 +  loop
 +      move relative 2 in c;
 +      if not found then
 +          exit;
 +      end if;
 +      fetch next from c into x;
 +      if found then
 +          return next x;
 +      end if;
 +  end loop;
 +  close c;
 +end;
 +$$ language plpgsql;
 +select * from sc_test() order by 1;
 + sc_test 
 +---------
 +       3
 +       6
 +       9
 +(3 rows)
 +
 +create or replace function sc_test() returns setof integer as $$
 +declare
 +  c cursor for select * from generate_series(1, 10);
 +  x integer;
 +begin
 +  open c;
 +  move forward all in c;
 +  fetch backward from c into x;
 +  if found then
 +    return next x;
 +  end if;
 +  close c;
 +end;
 +$$ language plpgsql;
 +select * from sc_test() order by 1;
 + sc_test 
 +---------
 +      10
 +(1 row)
 +
 +drop function sc_test();
 +-- test qualified variable names
 +create function pl_qual_names (param1 int) returns void as $$
 +<<outerblock>>
 +declare
 +  param1 int := 1;
 +begin
 +  <<innerblock>>
 +  declare
 +    param1 int := 2;
 +  begin
 +    raise notice 'param1 = %', param1;
 +    raise notice 'pl_qual_names.param1 = %', pl_qual_names.param1;
 +    raise notice 'outerblock.param1 = %', outerblock.param1;
 +    raise notice 'innerblock.param1 = %', innerblock.param1;
 +  end;
 +end;
 +$$ language plpgsql;
 +select pl_qual_names(42);
 +NOTICE:  param1 = 2
 +NOTICE:  pl_qual_names.param1 = 42
 +NOTICE:  outerblock.param1 = 1
 +NOTICE:  innerblock.param1 = 2
 + pl_qual_names 
 +---------------
 + 
 +(1 row)
 +
 +drop function pl_qual_names(int);
 +-- tests for RETURN QUERY
 +create function ret_query1(out int, out int) returns setof record as $$
 +begin
 +    $1 := -1;
 +    $2 := -2;
 +    return next;
 +    return query select x + 1, x * 10 from generate_series(0, 10) s (x);
 +    return next;
 +end;
 +$$ language plpgsql;
 +select * from ret_query1() order by 1, 2;
 + column1 | column2 
 +---------+---------
 +      -1 |      -2
 +      -1 |      -2
 +       1 |       0
 +       2 |      10
 +       3 |      20
 +       4 |      30
 +       5 |      40
 +       6 |      50
 +       7 |      60
 +       8 |      70
 +       9 |      80
 +      10 |      90
 +      11 |     100
 +(13 rows)
 +
 +create type record_type as (x text, y int, z boolean);
 +create or replace function ret_query2(lim int) returns setof record_type as $$
 +begin
 +    return query select md5(s.x::text), s.x, s.x > 0
 +                 from generate_series(-8, lim) s (x) where s.x % 2 = 0;
 +end;
 +$$ language plpgsql;
 +select * from ret_query2(8) order by 1;
 +                x                 | y  | z 
 +----------------------------------+----+---
 + 0267aaf632e87a63288a08331f22c7c3 | -4 | f
 + 1679091c5a880faf6fb5e6087eb1b2dc |  6 | t
 + 596a3d04481816330f07e4f97510c28f | -6 | f
 + 5d7b9adcbe1c629ec722529dd12e5129 | -2 | f
 + a87ff679a2f3e71d9181a67b7542122c |  4 | t
 + a8d2ec85eaf98407310b72eb73dda247 | -8 | f
 + c81e728d9d4c2f636f067f89cc14862c |  2 | t
 + c9f0f895fb98ab9159f51fd0297e236d |  8 | t
 + cfcd208495d565ef66e7dff9f98764da |  0 | f
 +(9 rows)
 +
 +-- test EXECUTE USING
 +create function exc_using(int, text) returns int as $$
 +declare i int;
 +begin
 +  for i in execute 'select * from generate_series(1,$1)' using $1+1 loop
 +    raise notice '%', i;
 +  end loop;
 +  execute 'select $2 + $2*3 + length($1)' into i using $2,$1;
 +  return i;
 +end
 +$$ language plpgsql;
 +select exc_using(5, 'foobar');
 +NOTICE:  1
 +NOTICE:  2
 +NOTICE:  3
 +NOTICE:  4
 +NOTICE:  5
 +NOTICE:  6
 + exc_using 
 +-----------
 +        26
 +(1 row)
 +
 +drop function exc_using(int, text);
 +create or replace function exc_using(int) returns void as $$
 +declare
 +  c refcursor;
 +  i int;
 +begin
 +  open c for execute 'select * from generate_series(1,$1)' using $1+1;
 +  loop
 +    fetch c into i;
 +    exit when not found;
 +    raise notice '%', i;
 +  end loop;
 +  close c;
 +  return;
 +end;
 +$$ language plpgsql;
 +select exc_using(5);
 +NOTICE:  1
 +NOTICE:  2
 +NOTICE:  3
 +NOTICE:  4
 +NOTICE:  5
 +NOTICE:  6
 + exc_using 
 +-----------
 + 
 +(1 row)
 +
 +drop function exc_using(int);
 +-- test FOR-over-cursor
 +create or replace function forc01() returns void as $$
 +declare
 +  c cursor(r1 integer, r2 integer)
 +       for select * from generate_series(r1,r2) i;
 +  c2 cursor
 +       for select * from generate_series(41,43) i;
 +begin
 +  for r in c(5,7) loop
 +    raise notice '% from %', r.i, c;
 +  end loop;
 +  -- again, to test if cursor was closed properly
 +  for r in c(9,10) loop
 +    raise notice '% from %', r.i, c;
 +  end loop;
 +  -- and test a parameterless cursor
 +  for r in c2 loop
 +    raise notice '% from %', r.i, c2;
 +  end loop;
 +  -- and try it with a hand-assigned name
 +  raise notice 'after loop, c2 = %', c2;
 +  c2 := 'special_name';
 +  for r in c2 loop
 +    raise notice '% from %', r.i, c2;
 +  end loop;
 +  raise notice 'after loop, c2 = %', c2;
 +  -- and try it with a generated name
 +  -- (which we can't show in the output because it's variable)
 +  c2 := null;
 +  for r in c2 loop
 +    raise notice '%', r.i;
 +  end loop;
 +  raise notice 'after loop, c2 = %', c2;
 +  return;
 +end;
 +$$ language plpgsql;
 +select forc01();
 +NOTICE:  5 from c
 +NOTICE:  6 from c
 +NOTICE:  7 from c
 +NOTICE:  9 from c
 +NOTICE:  10 from c
 +NOTICE:  41 from c2
 +NOTICE:  42 from c2
 +NOTICE:  43 from c2
 +NOTICE:  after loop, c2 = c2
 +NOTICE:  41 from special_name
 +NOTICE:  42 from special_name
 +NOTICE:  43 from special_name
 +NOTICE:  after loop, c2 = special_name
 +NOTICE:  41
 +NOTICE:  42
 +NOTICE:  43
 +NOTICE:  after loop, c2 = <NULL>
 + forc01 
 +--------
 + 
 +(1 row)
 +
 +-- try updating the cursor's current row
 +create temp table forc_test as
 +  select n as i, n as j from generate_series(1,10) n;
 +create or replace function forc01() returns void as $$
 +declare
 +  c cursor for select * from forc_test;
 +begin
 +  for r in c loop
 +    raise notice '%, %', r.i, r.j;
 +    update forc_test set i = i * 100, j = r.j * 2 where current of c;
 +  end loop;
 +end;
 +$$ language plpgsql;
 +select forc01();
 +NOTICE:  1, 1
 +ERROR:  WHERE CURRENT OF clause not yet supported
 +CONTEXT:  SQL statement "update forc_test set i = i * 100, j = r.j * 2 where current of c"
 +PL/pgSQL function forc01() line 7 at SQL statement
 +select * from forc_test order by 1, 2;
 + i  | j  
 +----+----
 +  1 |  1
 +  2 |  2
 +  3 |  3
 +  4 |  4
 +  5 |  5
 +  6 |  6
 +  7 |  7
 +  8 |  8
 +  9 |  9
 + 10 | 10
 +(10 rows)
 +
 +-- same, with a cursor whose portal name doesn't match variable name
 +create or replace function forc01() returns void as $$
 +declare
 +  c refcursor := 'fooled_ya';
 +  r record;
 +begin
 +  open c for select * from forc_test;
 +  loop
 +    fetch c into r;
 +    exit when not found;
 +    raise notice '%, %', r.i, r.j;
 +    update forc_test set i = i * 100, j = r.j * 2 where current of c;
 +  end loop;
 +end;
 +$$ language plpgsql;
 +select forc01();
 +NOTICE:  1, 1
 +ERROR:  WHERE CURRENT OF clause not yet supported
 +CONTEXT:  SQL statement "update forc_test set i = i * 100, j = r.j * 2 where current of c"
 +PL/pgSQL function forc01() line 11 at SQL statement
 +select * from forc_test order by 1;
 + i  | j  
 +----+----
 +  1 |  1
 +  2 |  2
 +  3 |  3
 +  4 |  4
 +  5 |  5
 +  6 |  6
 +  7 |  7
 +  8 |  8
 +  9 |  9
 + 10 | 10
 +(10 rows)
 +
 +drop function forc01();
 +-- fail because cursor has no query bound to it
 +create or replace function forc_bad() returns void as $$
 +declare
 +  c refcursor;
 +begin
 +  for r in c loop
 +    raise notice '%', r.i;
 +  end loop;
 +end;
 +$$ language plpgsql;
 +ERROR:  cursor FOR loop must use a bound cursor variable
 +LINE 5:   for r in c loop
 +                   ^
 +-- test RETURN QUERY EXECUTE
 +create or replace function return_dquery()
 +returns setof int as $$
 +begin
 +  return query execute 'select * from (values(10),(20)) f';
 +  return query execute 'select * from (values($1),($2)) f' using 40,50;
 +end;
 +$$ language plpgsql;
 +select * from return_dquery() order by 1;
 + return_dquery 
 +---------------
 +            10
 +            20
 +            40
 +            50
 +(4 rows)
 +
 +drop function return_dquery();
 +-- test RETURN QUERY with dropped columns
 +create table tabwithcols(a int, b int, c int, d int);
 +insert into tabwithcols values(10,20,30,40),(50,60,70,80);
 +create or replace function returnqueryf()
 +returns setof tabwithcols as $$
 +begin
 +  return query select * from tabwithcols;
 +  return query execute 'select * from tabwithcols';
 +end;
 +$$ language plpgsql;
 +select * from returnqueryf() order by 1,2,3,4;
 + a  | b  | c  | d  
 +----+----+----+----
 + 10 | 20 | 30 | 40
 + 10 | 20 | 30 | 40
 + 50 | 60 | 70 | 80
 + 50 | 60 | 70 | 80
 +(4 rows)
 +
 +alter table tabwithcols drop column b;
 +select * from returnqueryf() order by 1,2,3;
 + a  | c  | d  
 +----+----+----
 + 10 | 30 | 40
 + 10 | 30 | 40
 + 50 | 70 | 80
 + 50 | 70 | 80
 +(4 rows)
 +
 +alter table tabwithcols drop column d;
 +select * from returnqueryf() order by 1,2;
 + a  | c  
 +----+----
 + 10 | 30
 + 10 | 30
 + 50 | 70
 + 50 | 70
 +(4 rows)
 +
 +alter table tabwithcols add column d int;
 +select * from returnqueryf() order by 1,2,3;
 + a  | c  | d 
 +----+----+---
 + 10 | 30 |  
 + 10 | 30 |  
 + 50 | 70 |  
 + 50 | 70 |  
 +(4 rows)
 +
 +drop function returnqueryf();
 +drop table tabwithcols;
 +--
 +-- Tests for composite-type results
 +--
 +create type compostype as (x int, y varchar);
 +-- test: use of variable of composite type in return statement
 +create or replace function compos() returns compostype as $$
 +declare
 +  v compostype;
 +begin
 +  v := (1, 'hello');
 +  return v;
 +end;
 +$$ language plpgsql;
 +select compos();
 +  compos   
 +-----------
 + (1,hello)
 +(1 row)
 +
 +-- test: use of variable of record type in return statement
 +create or replace function compos() returns compostype as $$
 +declare
 +  v record;
 +begin
 +  v := (1, 'hello'::varchar);
 +  return v;
 +end;
 +$$ language plpgsql;
 +select compos();
 +  compos   
 +-----------
 + (1,hello)
 +(1 row)
 +
 +-- test: use of row expr in return statement
 +create or replace function compos() returns compostype as $$
 +begin
 +  return (1, 'hello'::varchar);
 +end;
 +$$ language plpgsql;
 +select compos();
 +  compos   
 +-----------
 + (1,hello)
 +(1 row)
 +
 +-- this does not work currently (no implicit casting)
 +create or replace function compos() returns compostype as $$
 +begin
 +  return (1, 'hello');
 +end;
 +$$ language plpgsql;
 +select compos();
 +ERROR:  returned record type does not match expected record type
 +DETAIL:  Returned type unknown does not match expected type character varying in column 2.
 +CONTEXT:  PL/pgSQL function compos() while casting return value to function's return type
 +-- ... but this does
 +create or replace function compos() returns compostype as $$
 +begin
 +  return (1, 'hello')::compostype;
 +end;
 +$$ language plpgsql;
 +select compos();
 +  compos   
 +-----------
 + (1,hello)
 +(1 row)
 +
 +drop function compos();
 +-- test: return a row expr as record.
 +create or replace function composrec() returns record as $$
 +declare
 +  v record;
 +begin
 +  v := (1, 'hello');
 +  return v;
 +end;
 +$$ language plpgsql;
 +select composrec();
 + composrec 
 +-----------
 + (1,hello)
 +(1 row)
 +
 +-- test: return row expr in return statement.
 +create or replace function composrec() returns record as $$
 +begin
 +  return (1, 'hello');
 +end;
 +$$ language plpgsql;
 +select composrec();
 + composrec 
 +-----------
 + (1,hello)
 +(1 row)
 +
 +drop function composrec();
 +-- test: row expr in RETURN NEXT statement.
 +create or replace function compos() returns setof compostype as $$
 +begin
 +  for i in 1..3
 +  loop
 +    return next (1, 'hello'::varchar);
 +  end loop;
 +  return next null::compostype;
 +  return next (2, 'goodbye')::compostype;
 +end;
 +$$ language plpgsql;
 +select * from compos();
 + x |    y    
 +---+---------
 + 1 | hello
 + 1 | hello
 + 1 | hello
 +   | 
 + 2 | goodbye
 +(5 rows)
 +
 +drop function compos();
 +-- test: use invalid expr in return statement.
 +create or replace function compos() returns compostype as $$
 +begin
 +  return 1 + 1;
 +end;
 +$$ language plpgsql;
 +select compos();
 +ERROR:  cannot return non-composite value from function returning composite type
 +CONTEXT:  PL/pgSQL function compos() line 3 at RETURN
 +-- RETURN variable is a different code path ...
 +create or replace function compos() returns compostype as $$
 +declare x int := 42;
 +begin
 +  return x;
 +end;
 +$$ language plpgsql;
 +select * from compos();
 +ERROR:  cannot return non-composite value from function returning composite type
 +CONTEXT:  PL/pgSQL function compos() line 4 at RETURN
 +drop function compos();
 +-- test: invalid use of composite variable in scalar-returning function
 +create or replace function compos() returns int as $$
 +declare
 +  v compostype;
 +begin
 +  v := (1, 'hello');
 +  return v;
 +end;
 +$$ language plpgsql;
 +select compos();
 +ERROR:  invalid input syntax for integer: "(1,hello)"
 +CONTEXT:  PL/pgSQL function compos() while casting return value to function's return type
 +-- test: invalid use of composite expression in scalar-returning function
 +create or replace function compos() returns int as $$
 +begin
 +  return (1, 'hello')::compostype;
 +end;
 +$$ language plpgsql;
 +select compos();
 +ERROR:  invalid input syntax for integer: "(1,hello)"
 +CONTEXT:  PL/pgSQL function compos() while casting return value to function's return type
 +drop function compos();
 +drop type compostype;
 +--
 +-- Tests for 8.4's new RAISE features
 +--
 +create or replace function raise_test() returns void as $$
 +begin
 +  raise notice '% % %', 1, 2, 3
 +     using errcode = '55001', detail = 'some detail info', hint = 'some hint';
 +  raise '% % %', 1, 2, 3
 +     using errcode = 'division_by_zero', detail = 'some detail info';
 +end;
 +$$ language plpgsql;
 +select raise_test();
 +NOTICE:  1 2 3
 +DETAIL:  some detail info
 +HINT:  some hint
 +ERROR:  1 2 3
 +DETAIL:  some detail info
 +-- Since we can't actually see the thrown SQLSTATE in default psql output,
 +-- test it like this; this also tests re-RAISE
 +create or replace function raise_test() returns void as $$
 +begin
 +  raise 'check me'
 +     using errcode = 'division_by_zero', detail = 'some detail info';
 +  exception
 +    when others then
 +      raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
 +      raise;
 +end;
 +$$ language plpgsql;
 +select raise_test();
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function raise_test() line 2 during statement block entry
 +create or replace function raise_test() returns void as $$
 +begin
 +  raise 'check me'
 +     using errcode = '1234F', detail = 'some detail info';
 +  exception
 +    when others then
 +      raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
 +      raise;
 +end;
 +$$ language plpgsql;
 +select raise_test();
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function raise_test() line 2 during statement block entry
 +-- SQLSTATE specification in WHEN
 +create or replace function raise_test() returns void as $$
 +begin
 +  raise 'check me'
 +     using errcode = '1234F', detail = 'some detail info';
 +  exception
 +    when sqlstate '1234F' then
 +      raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
 +      raise;
 +end;
 +$$ language plpgsql;
 +select raise_test();
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function raise_test() line 2 during statement block entry
 +create or replace function raise_test() returns void as $$
 +begin
 +  raise division_by_zero using detail = 'some detail info';
 +  exception
 +    when others then
 +      raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
 +      raise;
 +end;
 +$$ language plpgsql;
 +select raise_test();
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function raise_test() line 2 during statement block entry
 +create or replace function raise_test() returns void as $$
 +begin
 +  raise division_by_zero;
 +end;
 +$$ language plpgsql;
 +select raise_test();
 +ERROR:  division_by_zero
 +create or replace function raise_test() returns void as $$
 +begin
 +  raise sqlstate '1234F';
 +end;
 +$$ language plpgsql;
 +select raise_test();
 +ERROR:  1234F
 +create or replace function raise_test() returns void as $$
 +begin
 +  raise division_by_zero using message = 'custom' || ' message';
 +end;
 +$$ language plpgsql;
 +select raise_test();
 +ERROR:  custom message
 +create or replace function raise_test() returns void as $$
 +begin
 +  raise using message = 'custom' || ' message', errcode = '22012';
 +end;
 +$$ language plpgsql;
 +select raise_test();
 +ERROR:  custom message
 +-- conflict on message
 +create or replace function raise_test() returns void as $$
 +begin
 +  raise notice 'some message' using message = 'custom' || ' message', errcode = '22012';
 +end;
 +$$ language plpgsql;
 +select raise_test();
 +ERROR:  RAISE option already specified: MESSAGE
 +CONTEXT:  PL/pgSQL function raise_test() line 3 at RAISE
 +-- conflict on errcode
 +create or replace function raise_test() returns void as $$
 +begin
 +  raise division_by_zero using message = 'custom' || ' message', errcode = '22012';
 +end;
 +$$ language plpgsql;
 +select raise_test();
 +ERROR:  RAISE option already specified: ERRCODE
 +CONTEXT:  PL/pgSQL function raise_test() line 3 at RAISE
 +-- nothing to re-RAISE
 +create or replace function raise_test() returns void as $$
 +begin
 +  raise;
 +end;
 +$$ language plpgsql;
 +select raise_test();
 +ERROR:  RAISE without parameters cannot be used outside an exception handler
 +CONTEXT:  PL/pgSQL function raise_test() line 3 at RAISE
 +-- test access to exception data
 +create function zero_divide() returns int as $$
 +declare v int := 0;
 +begin
 +  return 10 / v;
 +end;
 +$$ language plpgsql;
 +create or replace function raise_test() returns void as $$
 +begin
 +  raise exception 'custom exception'
 +     using detail = 'some detail of custom exception',
 +           hint = 'some hint related to custom exception';
 +end;
 +$$ language plpgsql;
 +create function stacked_diagnostics_test() returns void as $$
 +declare _sqlstate text;
 +        _message text;
 +        _context text;
 +begin
 +  perform zero_divide();
 +exception when others then
 +  get stacked diagnostics
 +        _sqlstate = returned_sqlstate,
 +        _message = message_text,
 +        _context = pg_exception_context;
 +  raise notice 'sqlstate: %, message: %, context: [%]',
 +    _sqlstate, _message, replace(_context, E'\n', ' <- ');
 +end;
 +$$ language plpgsql;
 +select stacked_diagnostics_test();
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function stacked_diagnostics_test() line 5 during statement block entry
 +create or replace function stacked_diagnostics_test() returns void as $$
 +declare _detail text;
 +        _hint text;
 +        _message text;
 +begin
 +  perform raise_test();
 +exception when others then
 +  get stacked diagnostics
 +        _message = message_text,
 +        _detail = pg_exception_detail,
 +        _hint = pg_exception_hint;
 +  raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint;
 +end;
 +$$ language plpgsql;
 +select stacked_diagnostics_test();
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function stacked_diagnostics_test() line 5 during statement block entry
 +-- fail, cannot use stacked diagnostics statement outside handler
 +create or replace function stacked_diagnostics_test() returns void as $$
 +declare _detail text;
 +        _hint text;
 +        _message text;
 +begin
 +  get stacked diagnostics
 +        _message = message_text,
 +        _detail = pg_exception_detail,
 +        _hint = pg_exception_hint;
 +  raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint;
 +end;
 +$$ language plpgsql;
 +select stacked_diagnostics_test();
 +ERROR:  GET STACKED DIAGNOSTICS cannot be used outside an exception handler
 +CONTEXT:  PL/pgSQL function stacked_diagnostics_test() line 6 at GET STACKED DIAGNOSTICS
 +drop function zero_divide();
 +drop function stacked_diagnostics_test();
 +-- check cases where implicit SQLSTATE variable could be confused with
 +-- SQLSTATE as a keyword, cf bug #5524
 +create or replace function raise_test() returns void as $$
 +begin
 +  perform 1/0;
 +exception
 +  when sqlstate '22012' then
 +    raise notice using message = sqlstate;
 +    raise sqlstate '22012' using message = 'substitute message';
 +end;
 +$$ language plpgsql;
 +select raise_test();
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function raise_test() line 2 during statement block entry
 +drop function raise_test();
 +-- test passing column_name, constraint_name, datatype_name, table_name
 +-- and schema_name error fields
 +create or replace function stacked_diagnostics_test() returns void as $$
 +declare _column_name text;
 +        _constraint_name text;
 +        _datatype_name text;
 +        _table_name text;
 +        _schema_name text;
 +begin
 +  raise exception using
 +    column = '>>some column name<<',
 +    constraint = '>>some constraint name<<',
 +    datatype = '>>some datatype name<<',
 +    table = '>>some table name<<',
 +    schema = '>>some schema name<<';
 +exception when others then
 +  get stacked diagnostics
 +        _column_name = column_name,
 +        _constraint_name = constraint_name,
 +        _datatype_name = pg_datatype_name,
 +        _table_name = table_name,
 +        _schema_name = schema_name;
 +  raise notice 'column %, constraint %, type %, table %, schema %',
 +    _column_name, _constraint_name, _datatype_name, _table_name, _schema_name;
 +end;
 +$$ language plpgsql;
 +select stacked_diagnostics_test();
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function stacked_diagnostics_test() line 7 during statement block entry
 +drop function stacked_diagnostics_test();
 +-- test CASE statement
 +create or replace function case_test(bigint) returns text as $$
 +declare a int = 10;
 +        b int = 1;
 +begin
 +  case $1
 +    when 1 then
 +      return 'one';
 +    when 2 then
 +      return 'two';
 +    when 3,4,3+5 then
 +      return 'three, four or eight';
 +    when a then
 +      return 'ten';
 +    when a+b, a+b+1 then
 +      return 'eleven, twelve';
 +  end case;
 +end;
 +$$ language plpgsql immutable;
 +select case_test(1);
 + case_test 
 +-----------
 + one
 +(1 row)
 +
 +select case_test(2);
 + case_test 
 +-----------
 + two
 +(1 row)
 +
 +select case_test(3);
 +      case_test       
 +----------------------
 + three, four or eight
 +(1 row)
 +
 +select case_test(4);
 +      case_test       
 +----------------------
 + three, four or eight
 +(1 row)
 +
 +select case_test(5); -- fails
 +ERROR:  case not found
 +HINT:  CASE statement is missing ELSE part.
 +CONTEXT:  PL/pgSQL function case_test(bigint) line 5 at CASE
 +select case_test(8);
 +      case_test       
 +----------------------
 + three, four or eight
 +(1 row)
 +
 +select case_test(10);
 + case_test 
 +-----------
 + ten
 +(1 row)
 +
 +select case_test(11);
 +   case_test    
 +----------------
 + eleven, twelve
 +(1 row)
 +
 +select case_test(12);
 +   case_test    
 +----------------
 + eleven, twelve
 +(1 row)
 +
 +select case_test(13); -- fails
 +ERROR:  case not found
 +HINT:  CASE statement is missing ELSE part.
 +CONTEXT:  PL/pgSQL function case_test(bigint) line 5 at CASE
 +create or replace function catch() returns void as $$
 +begin
 +  raise notice '%', case_test(6);
 +exception
 +  when case_not_found then
 +    raise notice 'caught case_not_found % %', SQLSTATE, SQLERRM;
 +end
 +$$ language plpgsql;
 +select catch();
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function catch() line 2 during statement block entry
 +-- test the searched variant too, as well as ELSE
 +create or replace function case_test(bigint) returns text as $$
 +declare a int = 10;
 +begin
 +  case
 +    when $1 = 1 then
 +      return 'one';
 +    when $1 = a + 2 then
 +      return 'twelve';
 +    else
 +      return 'other';
 +  end case;
 +end;
 +$$ language plpgsql immutable;
 +select case_test(1);
 + case_test 
 +-----------
 + one
 +(1 row)
 +
 +select case_test(2);
 + case_test 
 +-----------
 + other
 +(1 row)
 +
 +select case_test(12);
 + case_test 
 +-----------
 + twelve
 +(1 row)
 +
 +select case_test(13);
 + case_test 
 +-----------
 + other
 +(1 row)
 +
 +drop function catch();
 +drop function case_test(bigint);
 +-- test variadic functions
 +create or replace function vari(variadic int[])
 +returns void as $$
 +begin
 +  for i in array_lower($1,1)..array_upper($1,1) loop
 +    raise notice '%', $1[i];
 +  end loop; end;
 +$$ language plpgsql;
 +select vari(1,2,3,4,5);
 +NOTICE:  1
 +NOTICE:  2
 +NOTICE:  3
 +NOTICE:  4
 +NOTICE:  5
 + vari 
 +------
 + 
 +(1 row)
 +
 +select vari(3,4,5);
 +NOTICE:  3
 +NOTICE:  4
 +NOTICE:  5
 + vari 
 +------
 + 
 +(1 row)
 +
 +select vari(variadic array[5,6,7]);
 +NOTICE:  5
 +NOTICE:  6
 +NOTICE:  7
 + vari 
 +------
 + 
 +(1 row)
 +
 +drop function vari(int[]);
 +-- coercion test
 +create or replace function pleast(variadic numeric[])
 +returns numeric as $$
 +declare aux numeric = $1[array_lower($1,1)];
 +begin
 +  for i in array_lower($1,1)+1..array_upper($1,1) loop
 +    if $1[i] < aux then aux := $1[i]; end if;
 +  end loop;
 +  return aux;
 +end;
 +$$ language plpgsql immutable strict;
 +select pleast(10,1,2,3,-16);
 + pleast 
 +--------
 +    -16
 +(1 row)
 +
 +select pleast(10.2,2.2,-1.1);
 + pleast 
 +--------
 +   -1.1
 +(1 row)
 +
 +select pleast(10.2,10, -20);
 + pleast 
 +--------
 +    -20
 +(1 row)
 +
 +select pleast(10,20, -1.0);
 + pleast 
 +--------
 +   -1.0
 +(1 row)
 +
 +-- in case of conflict, non-variadic version is preferred
 +create or replace function pleast(numeric)
 +returns numeric as $$
 +begin
 +  raise notice 'non-variadic function called';
 +  return $1;
 +end;
 +$$ language plpgsql immutable strict;
 +select pleast(10);
 +NOTICE:  non-variadic function called
 + pleast 
 +--------
 +     10
 +(1 row)
 +
 +drop function pleast(numeric[]);
 +drop function pleast(numeric);
 +-- test table functions
 +create function tftest(int) returns table(a int, b int) as $$
 +begin
 +  return query select $1, $1+i from generate_series(1,5) g(i);
 +end;
 +$$ language plpgsql immutable strict;
 +select * from tftest(10) order by 1, 2;
 + a  | b  
 +----+----
 + 10 | 11
 + 10 | 12
 + 10 | 13
 + 10 | 14
 + 10 | 15
 +(5 rows)
 +
 +create or replace function tftest(a1 int) returns table(a int, b int) as $$
 +begin
 +  a := a1; b := a1 + 1;
 +  return next;
 +  a := a1 * 10; b := a1 * 10 + 1;
 +  return next;
 +end;
 +$$ language plpgsql immutable strict;
 +select * from tftest(10) order by 1, 2;
 +  a  |  b  
 +-----+-----
 +  10 |  11
 + 100 | 101
 +(2 rows)
 +
 +drop function tftest(int);
 +create or replace function rttest()
 +returns setof int as $$
 +declare rc int;
 +begin
 +  return query values(10),(20);
 +  get diagnostics rc = row_count;
 +  raise notice '% %', found, rc;
 +  return query select * from (values(10),(20)) f(a) where false;
 +  get diagnostics rc = row_count;
 +  raise notice '% %', found, rc;
 +  return query execute 'values(10),(20)';
 +  get diagnostics rc = row_count;
 +  raise notice '% %', found, rc;
 +  return query execute 'select * from (values(10),(20)) f(a) where false';
 +  get diagnostics rc = row_count;
 +  raise notice '% %', found, rc;
 +end;
 +$$ language plpgsql;
 +select * from rttest() order by 1;
 +NOTICE:  t 2
 +NOTICE:  f 0
 +NOTICE:  t 2
 +NOTICE:  f 0
 + rttest 
 +--------
 +     10
 +     10
 +     20
 +     20
 +(4 rows)
 +
 +drop function rttest();
 +-- Test for proper cleanup at subtransaction exit.  This example
 +-- exposed a bug in PG 8.2.
 +CREATE FUNCTION leaker_1(fail BOOL) RETURNS INTEGER AS $$
 +DECLARE
 +  v_var INTEGER;
 +BEGIN
 +  BEGIN
 +    v_var := (leaker_2(fail)).error_code;
 +  EXCEPTION
 +    WHEN others THEN RETURN 0;
 +  END;
 +  RETURN 1;
 +END;
 +$$ LANGUAGE plpgsql;
 +CREATE FUNCTION leaker_2(fail BOOL, OUT error_code INTEGER, OUT new_id INTEGER)
 +  RETURNS RECORD AS $$
 +BEGIN
 +  IF fail THEN
 +    RAISE EXCEPTION 'fail ...';
 +  END IF;
 +  error_code := 1;
 +  new_id := 1;
 +  RETURN;
 +END;
 +$$ LANGUAGE plpgsql;
 +SELECT * FROM leaker_1(false);
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function leaker_1(boolean) line 5 during statement block entry
 +SELECT * FROM leaker_1(true);
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function leaker_1(boolean) line 5 during statement block entry
 +DROP FUNCTION leaker_1(bool);
 +DROP FUNCTION leaker_2(bool);
 +-- Test for appropriate cleanup of non-simple expression evaluations
 +-- (bug in all versions prior to August 2010)
 +CREATE FUNCTION nonsimple_expr_test() RETURNS text[] AS $$
 +DECLARE
 +  arr text[];
 +  lr text;
 +  i integer;
 +BEGIN
 +  arr := array[array['foo','bar'], array['baz', 'quux']];
 +  lr := 'fool';
 +  i := 1;
 +  -- use sub-SELECTs to make expressions non-simple
 +  arr[(SELECT i)][(SELECT i+1)] := (SELECT lr);
 +  RETURN arr;
 +END;
 +$$ LANGUAGE plpgsql;
 +SELECT nonsimple_expr_test();
 +   nonsimple_expr_test   
 +-------------------------
 + {{foo,fool},{baz,quux}}
 +(1 row)
 +
 +DROP FUNCTION nonsimple_expr_test();
 +CREATE FUNCTION nonsimple_expr_test() RETURNS integer AS $$
 +declare
 +   i integer NOT NULL := 0;
 +begin
 +  begin
 +    i := (SELECT NULL::integer);  -- should throw error
 +  exception
 +    WHEN OTHERS THEN
 +      i := (SELECT 1::integer);
 +  end;
 +  return i;
 +end;
 +$$ LANGUAGE plpgsql;
 +SELECT nonsimple_expr_test();
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function nonsimple_expr_test() line 5 during statement block entry
 +DROP FUNCTION nonsimple_expr_test();
 +--
 +-- Test cases involving recursion and error recovery in simple expressions
 +-- (bugs in all versions before October 2010).  The problems are most
 +-- easily exposed by mutual recursion between plpgsql and sql functions.
 +--
 +create function recurse(float8) returns float8 as
 +$$
 +begin
 +  if ($1 > 0) then
 +    return sql_recurse($1 - 1);
 +  else
 +    return $1;
 +  end if;
 +end;
 +$$ language plpgsql;
 +-- "limit" is to prevent this from being inlined
 +create function sql_recurse(float8) returns float8 as
 +$$ select recurse($1) limit 1; $$ language sql;
 +select recurse(10);
 + recurse 
 +---------
 +       0
 +(1 row)
 +
 +create function error1(text) returns text language sql as
 +$$ SELECT relname::text FROM pg_class c WHERE c.oid = $1::regclass $$;
 +create function error2(p_name_table text) returns text language plpgsql as $$
 +begin
 +  return error1(p_name_table);
 +end$$;
 +BEGIN;
 +create table public.stuffs (stuff text);
 +SAVEPOINT a;
 +ERROR:  SAVEPOINT is not yet supported.
 +select error2('nonexistent.stuffs');
 +ERROR:  current transaction is aborted, commands ignored until end of transaction block
 +ROLLBACK TO a;
 +ERROR:  no such savepoint
 +select error2('public.stuffs');
 +ERROR:  current transaction is aborted, commands ignored until end of transaction block
 +rollback;
 +drop function error2(p_name_table text);
 +drop function error1(text);
 +-- Test for proper handling of cast-expression caching
 +create function sql_to_date(integer) returns date as $$
 +select $1::text::date
 +$$ language sql immutable strict;
 +create cast (integer as date) with function sql_to_date(integer) as assignment;
 +create function cast_invoker(integer) returns date as $$
 +begin
 +  return $1;
 +end$$ language plpgsql;
 +select cast_invoker(20150717);
 + cast_invoker 
 +--------------
 + 07-17-2015
 +(1 row)
 +
 +select cast_invoker(20150718);  -- second call crashed in pre-release 9.5
 + cast_invoker 
 +--------------
 + 07-18-2015
 +(1 row)
 +
 +begin;
 +select cast_invoker(20150717);
 + cast_invoker 
 +--------------
 + 07-17-2015
 +(1 row)
 +
 +select cast_invoker(20150718);
 + cast_invoker 
 +--------------
 + 07-18-2015
 +(1 row)
 +
 +savepoint s1;
 +ERROR:  SAVEPOINT is not yet supported.
 +select cast_invoker(20150718);
 +ERROR:  current transaction is aborted, commands ignored until end of transaction block
 +select cast_invoker(-1); -- fails
 +ERROR:  current transaction is aborted, commands ignored until end of transaction block
 +rollback to savepoint s1;
 +ERROR:  no such savepoint
 +select cast_invoker(20150719);
 +ERROR:  current transaction is aborted, commands ignored until end of transaction block
 +select cast_invoker(20150720);
 +ERROR:  current transaction is aborted, commands ignored until end of transaction block
 +commit;
 +drop function cast_invoker(integer);
 +drop function sql_to_date(integer) cascade;
 +NOTICE:  drop cascades to cast from integer to date
 +-- Test handling of cast cache inside DO blocks
 +-- (to check the original crash case, this must be a cast not previously
 +-- used in this session)
 +begin;
 +do $$ declare x text[]; begin x := '{1.23, 4.56}'::numeric[]; end $$;
 +do $$ declare x text[]; begin x := '{1.23, 4.56}'::numeric[]; end $$;
 +end;
 +-- Test for consistent reporting of error context
 +create function fail() returns int language plpgsql as $$
 +begin
 +  return 1/0;
 +end
 +$$;
 +select fail();
 +ERROR:  division by zero
 +CONTEXT:  SQL statement "SELECT 1/0"
 +PL/pgSQL function fail() line 3 at RETURN
 +select fail();
 +ERROR:  division by zero
 +CONTEXT:  SQL statement "SELECT 1/0"
 +PL/pgSQL function fail() line 3 at RETURN
 +drop function fail();
 +-- Test handling of string literals.
 +set standard_conforming_strings = off;
 +create or replace function strtest() returns text as $$
 +begin
 +  raise notice 'foo\\bar\041baz';
 +  return 'foo\\bar\041baz';
 +end
 +$$ language plpgsql;
 +WARNING:  nonstandard use of \\ in a string literal
 +LINE 3:   raise notice 'foo\\bar\041baz';
 +                       ^
 +HINT:  Use the escape string syntax for backslashes, e.g., E'\\'.
 +WARNING:  nonstandard use of \\ in a string literal
 +LINE 4:   return 'foo\\bar\041baz';
 +                 ^
 +HINT:  Use the escape string syntax for backslashes, e.g., E'\\'.
 +WARNING:  nonstandard use of \\ in a string literal
 +LINE 4:   return 'foo\\bar\041baz';
 +                 ^
 +HINT:  Use the escape string syntax for backslashes, e.g., E'\\'.
 +select strtest();
 +NOTICE:  foo\bar!baz
 +WARNING:  nonstandard use of \\ in a string literal
 +LINE 1: SELECT 'foo\\bar\041baz'
 +               ^
 +HINT:  Use the escape string syntax for backslashes, e.g., E'\\'.
 +QUERY:  SELECT 'foo\\bar\041baz'
 +CONTEXT:  PL/pgSQL function strtest() line 4 at RETURN
 +   strtest   
 +-------------
 + foo\bar!baz
 +(1 row)
 +
 +create or replace function strtest() returns text as $$
 +begin
 +  raise notice E'foo\\bar\041baz';
 +  return E'foo\\bar\041baz';
 +end
 +$$ language plpgsql;
 +select strtest();
 +NOTICE:  foo\bar!baz
 +   strtest   
 +-------------
 + foo\bar!baz
 +(1 row)
 +
 +set standard_conforming_strings = on;
 +create or replace function strtest() returns text as $$
 +begin
 +  raise notice 'foo\\bar\041baz\';
 +  return 'foo\\bar\041baz\';
 +end
 +$$ language plpgsql;
 +select strtest();
 +NOTICE:  foo\\bar\041baz\
 +     strtest      
 +------------------
 + foo\\bar\041baz\
 +(1 row)
 +
 +create or replace function strtest() returns text as $$
 +begin
 +  raise notice E'foo\\bar\041baz';
 +  return E'foo\\bar\041baz';
 +end
 +$$ language plpgsql;
 +select strtest();
 +NOTICE:  foo\bar!baz
 +   strtest   
 +-------------
 + foo\bar!baz
 +(1 row)
 +
 +drop function strtest();
 +-- Test anonymous code blocks.
 +DO $$
 +DECLARE r record;
 +BEGIN
 +    FOR r IN SELECT rtrim(roomno) AS roomno, comment FROM Room ORDER BY roomno
 +    LOOP
 +        RAISE NOTICE '%, %', r.roomno, r.comment;
 +    END LOOP;
 +END$$;
 +NOTICE:  001, Entrance
 +NOTICE:  002, Office
 +NOTICE:  003, Office
 +NOTICE:  004, Technical
 +NOTICE:  101, Office
 +NOTICE:  102, Conference
 +NOTICE:  103, Restroom
 +NOTICE:  104, Technical
 +NOTICE:  105, Office
 +NOTICE:  106, Office
 +-- these are to check syntax error reporting
 +DO LANGUAGE plpgsql $$begin return 1; end$$;
 +ERROR:  RETURN cannot have a parameter in function returning void
 +LINE 1: DO LANGUAGE plpgsql $$begin return 1; end$$;
 +                                           ^
 +DO $$
 +DECLARE r record;
 +BEGIN
 +    FOR r IN SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomno
 +    LOOP
 +        RAISE NOTICE '%, %', r.roomno, r.comment;
 +    END LOOP;
 +END$$;
 +ERROR:  column "foo" does not exist
 +LINE 1: SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomn...
 +                                        ^
 +QUERY:  SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomno
 +CONTEXT:  PL/pgSQL function inline_code_block line 4 at FOR over SELECT rows
 +-- Check handling of errors thrown from/into anonymous code blocks.
 +do $outer$
 +begin
 +  for i in 1..10 loop
 +   begin
 +    execute $ex$
 +      do $$
 +      declare x int = 0;
 +      begin
 +        x := 1 / x;
 +      end;
 +      $$;
 +    $ex$;
 +  exception when division_by_zero then
 +    raise notice 'caught division by zero';
 +  end;
 +  end loop;
 +end;
 +$outer$;
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function inline_code_block line 4 during statement block entry
 +-- Check variable scoping -- a var is not available in its own or prior
 +-- default expressions.
 +create function scope_test() returns int as $$
 +declare x int := 42;
 +begin
 +  declare y int := x + 1;
 +          x int := x + 2;
 +  begin
 +    return x * 100 + y;
 +  end;
 +end;
 +$$ language plpgsql;
 +select scope_test();
 + scope_test 
 +------------
 +       4443
 +(1 row)
 +
 +drop function scope_test();
 +-- Check handling of conflicts between plpgsql vars and table columns.
 +set plpgsql.variable_conflict = error;
 +create function conflict_test() returns setof int8_tbl as $$
 +declare r record;
 +  q1 bigint := 42;
 +begin
 +  for r in select q1,q2 from int8_tbl loop
 +    return next r;
 +  end loop;
 +end;
 +$$ language plpgsql;
 +select * from conflict_test() order by 1,2;
 +ERROR:  column reference "q1" is ambiguous
 +LINE 1: select q1,q2 from int8_tbl
 +               ^
 +DETAIL:  It could refer to either a PL/pgSQL variable or a table column.
 +QUERY:  select q1,q2 from int8_tbl
 +CONTEXT:  PL/pgSQL function conflict_test() line 5 at FOR over SELECT rows
 +create or replace function conflict_test() returns setof int8_tbl as $$
 +#variable_conflict use_variable
 +declare r record;
 +  q1 bigint := 42;
 +begin
 +  for r in select q1,q2 from int8_tbl loop
 +    return next r;
 +  end loop;
 +end;
 +$$ language plpgsql;
 +-- PGXCTODO: This is failing due to issue 3522907, complicated SELECT queries in plpgsql functions
 +select * from conflict_test() order by 1,2;
 + q1 |        q2         
 +----+-------------------
 + 42 | -4567890123456789
 + 42 |               123
 + 42 |               456
 + 42 |  4567890123456789
 + 42 |  4567890123456789
 +(5 rows)
 +
 +create or replace function conflict_test() returns setof int8_tbl as $$
 +#variable_conflict use_column
 +declare r record;
 +  q1 bigint := 42;
 +begin
 +  for r in select q1,q2 from int8_tbl loop
 +    return next r;
 +  end loop;
 +end;
 +$$ language plpgsql;
 +select * from conflict_test() order by 1,2;
 +        q1        |        q2         
 +------------------+-------------------
 +              123 |               456
 +              123 |  4567890123456789
 + 4567890123456789 | -4567890123456789
 + 4567890123456789 |               123
 + 4567890123456789 |  4567890123456789
 +(5 rows)
 +
 +drop function conflict_test();
 +-- Check that an unreserved keyword can be used as a variable name
 +create function unreserved_test() returns int as $$
 +declare
 +  forward int := 21;
 +begin
 +  forward := forward * 2;
 +  return forward;
 +end
 +$$ language plpgsql;
 +select unreserved_test();
 + unreserved_test 
 +-----------------
 +              42
 +(1 row)
 +
 +create or replace function unreserved_test() returns int as $$
 +declare
 +  return int := 42;
 +begin
 +  return := return + 1;
 +  return return;
 +end
 +$$ language plpgsql;
 +select unreserved_test();
 + unreserved_test 
 +-----------------
 +              43
 +(1 row)
 +
 +drop function unreserved_test();
 +--
 +-- Test FOREACH over arrays
 +--
 +create function foreach_test(anyarray)
 +returns void as $$
 +declare x int;
 +begin
 +  foreach x in array $1
 +  loop
 +    raise notice '%', x;
 +  end loop;
 +  end;
 +$$ language plpgsql;
 +select foreach_test(ARRAY[1,2,3,4]);
 +NOTICE:  1
 +NOTICE:  2
 +NOTICE:  3
 +NOTICE:  4
 + foreach_test 
 +--------------
 + 
 +(1 row)
 +
 +select foreach_test(ARRAY[[1,2],[3,4]]);
 +NOTICE:  1
 +NOTICE:  2
 +NOTICE:  3
 +NOTICE:  4
 + foreach_test 
 +--------------
 + 
 +(1 row)
 +
 +create or replace function foreach_test(anyarray)
 +returns void as $$
 +declare x int;
 +begin
 +  foreach x slice 1 in array $1
 +  loop
 +    raise notice '%', x;
 +  end loop;
 +  end;
 +$$ language plpgsql;
 +-- should fail
 +select foreach_test(ARRAY[1,2,3,4]);
 +ERROR:  FOREACH ... SLICE loop variable must be of an array type
 +CONTEXT:  PL/pgSQL function foreach_test(anyarray) line 4 at FOREACH over array
 +select foreach_test(ARRAY[[1,2],[3,4]]);
 +ERROR:  FOREACH ... SLICE loop variable must be of an array type
 +CONTEXT:  PL/pgSQL function foreach_test(anyarray) line 4 at FOREACH over array
 +create or replace function foreach_test(anyarray)
 +returns void as $$
 +declare x int[];
 +begin
 +  foreach x slice 1 in array $1
 +  loop
 +    raise notice '%', x;
 +  end loop;
 +  end;
 +$$ language plpgsql;
 +select foreach_test(ARRAY[1,2,3,4]);
 +NOTICE:  {1,2,3,4}
 + foreach_test 
 +--------------
 + 
 +(1 row)
 +
 +select foreach_test(ARRAY[[1,2],[3,4]]);
 +NOTICE:  {1,2}
 +NOTICE:  {3,4}
 + foreach_test 
 +--------------
 + 
 +(1 row)
 +
 +-- higher level of slicing
 +create or replace function foreach_test(anyarray)
 +returns void as $$
 +declare x int[];
 +begin
 +  foreach x slice 2 in array $1
 +  loop
 +    raise notice '%', x;
 +  end loop;
 +  end;
 +$$ language plpgsql;
 +-- should fail
 +select foreach_test(ARRAY[1,2,3,4]);
 +ERROR:  slice dimension (2) is out of the valid range 0..1
 +CONTEXT:  PL/pgSQL function foreach_test(anyarray) line 4 at FOREACH over array
 +-- ok
 +select foreach_test(ARRAY[[1,2],[3,4]]);
 +NOTICE:  {{1,2},{3,4}}
 + foreach_test 
 +--------------
 + 
 +(1 row)
 +
 +select foreach_test(ARRAY[[[1,2]],[[3,4]]]);
 +NOTICE:  {{1,2}}
 +NOTICE:  {{3,4}}
 + foreach_test 
 +--------------
 + 
 +(1 row)
 +
 +create type xy_tuple AS (x int, y int);
 +-- iteration over array of records
 +create or replace function foreach_test(anyarray)
 +returns void as $$
 +declare r record;
 +begin
 +  foreach r in array $1
 +  loop
 +    raise notice '%', r;
 +  end loop;
 +  end;
 +$$ language plpgsql;
 +select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]);
 +NOTICE:  (10,20)
 +NOTICE:  (40,69)
 +NOTICE:  (35,78)
 + foreach_test 
 +--------------
 + 
 +(1 row)
 +
 +select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]);
 +NOTICE:  (10,20)
 +NOTICE:  (40,69)
 +NOTICE:  (35,78)
 +NOTICE:  (88,76)
 + foreach_test 
 +--------------
 + 
 +(1 row)
 +
 +create or replace function foreach_test(anyarray)
 +returns void as $$
 +declare x int; y int;
 +begin
 +  foreach x, y in array $1
 +  loop
 +    raise notice 'x = %, y = %', x, y;
 +  end loop;
 +  end;
 +$$ language plpgsql;
 +select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]);
 +NOTICE:  x = 10, y = 20
 +NOTICE:  x = 40, y = 69
 +NOTICE:  x = 35, y = 78
 + foreach_test 
 +--------------
 + 
 +(1 row)
 +
 +select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]);
 +NOTICE:  x = 10, y = 20
 +NOTICE:  x = 40, y = 69
 +NOTICE:  x = 35, y = 78
 +NOTICE:  x = 88, y = 76
 + foreach_test 
 +--------------
 + 
 +(1 row)
 +
 +-- slicing over array of composite types
 +create or replace function foreach_test(anyarray)
 +returns void as $$
 +declare x xy_tuple[];
 +begin
 +  foreach x slice 1 in array $1
 +  loop
 +    raise notice '%', x;
 +  end loop;
 +  end;
 +$$ language plpgsql;
 +select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]);
 +NOTICE:  {"(10,20)","(40,69)","(35,78)"}
 + foreach_test 
 +--------------
 + 
 +(1 row)
 +
 +select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]);
 +NOTICE:  {"(10,20)","(40,69)"}
 +NOTICE:  {"(35,78)","(88,76)"}
 + foreach_test 
 +--------------
 + 
 +(1 row)
 +
 +drop function foreach_test(anyarray);
 +drop type xy_tuple;
 +--
 +-- Assorted tests for array subscript assignment
 +--
 +create temp table rtype (id int, ar text[]);
 +create function arrayassign1() returns text[] language plpgsql as $$
 +declare
 + r record;
 +begin
 +  r := row(12, '{foo,bar,baz}')::rtype;
 +  r.ar[2] := 'replace';
 +  return r.ar;
 +end$$;
 +select arrayassign1();
 +   arrayassign1    
 +-------------------
 + {foo,replace,baz}
 +(1 row)
 +
 +select arrayassign1(); -- try again to exercise internal caching
 +   arrayassign1    
 +-------------------
 + {foo,replace,baz}
 +(1 row)
 +
 +create domain orderedarray as int[2]
 +  constraint sorted check (value[1] < value[2]);
 +select '{1,2}'::orderedarray;
 + orderedarray 
 +--------------
 + {1,2}
 +(1 row)
 +
 +select '{2,1}'::orderedarray;  -- fail
 +ERROR:  value for domain orderedarray violates check constraint "sorted"
 +create function testoa(x1 int, x2 int, x3 int) returns orderedarray
 +language plpgsql as $$
 +declare res orderedarray;
 +begin
 +  res := array[x1, x2];
 +  res[2] := x3;
 +  return res;
 +end$$;
 +select testoa(1,2,3);
 + testoa 
 +--------
 + {1,3}
 +(1 row)
 +
 +select testoa(1,2,3); -- try again to exercise internal caching
 + testoa 
 +--------
 + {1,3}
 +(1 row)
 +
 +select testoa(2,1,3); -- fail at initial assign
 +ERROR:  value for domain orderedarray violates check constraint "sorted"
 +CONTEXT:  PL/pgSQL function testoa(integer,integer,integer) line 4 at assignment
 +select testoa(1,2,1); -- fail at update
 +ERROR:  value for domain orderedarray violates check constraint "sorted"
 +CONTEXT:  PL/pgSQL function testoa(integer,integer,integer) line 5 at assignment
 +drop function arrayassign1();
 +drop function testoa(x1 int, x2 int, x3 int);
 +-- Check that DMLs in a plpgsql function work OK, when subsequent queries need
 +-- to open new datanode connections
 +CREATE OR REPLACE FUNCTION TestJoinTempTable_CT()
 +RETURNS void AS $$
 +BEGIN
 +        CREATE TABLE IF NOT EXISTS RealTable(ProductId int, ScenarioId int);
 +        TRUNCATE TABLE RealTable;
 +
 +        CREATE TABLE IF NOT EXISTS TmpBar(NodeId int)
 +                DISTRIBUTE BY REPLICATION;
 +        CREATE TABLE IF NOT EXISTS TmpFoo(TempId int)
 +                DISTRIBUTE BY REPLICATION;
 +END ;
 +$$ LANGUAGE plpgsql;
 +CREATE OR REPLACE FUNCTION TestJoinTempTable_INSERT()
 +RETURNS void AS $$
 +BEGIN
 +        INSERT INTO RealTable(ProductId, ScenarioId)
 +                SELECT generate_series(1,1000) as ProductId, (random() * 100)::int as ScenarioId;
 +
 +        INSERT INTO TmpBar(NodeId)
 +                SELECT generate_series(1,1000);
 +              RAISE INFO 'number of existing rows in RealTable - %', (SELECT count(*) FROM RealTable);
 +              RAISE INFO 'number of existing rows in TmpBar - %', (SELECT count(*) FROM TmpBar);
 +              RAISE INFO 'number of existing rows in TmpFoo - %', (SELECT count(*) FROM TmpFoo);
 +        INSERT INTO TmpFoo(TempId)
 +               SELECT DISTINCT(PR.ProductId)
 +                 FROM RealTable AS PR
 +                 JOIN TmpBar tmp1 ON PR.ProductId = tmp1.NodeId;
 +
 +              RAISE INFO 'number of rows produced by query - %',
 +                              (SELECT COUNT(DISTINCT(PR.ProductId))
 +                               FROM RealTable AS PR
 +                 JOIN TmpBar tmp1 ON PR.ProductId = tmp1.NodeId);
 +              RAISE INFO 'number of rows in TmpFoo - %', (SELECT count(*) FROM TmpFoo);
 +              RAISE INFO 'number of existing rows in TmpFoo - %', (SELECT count(*) FROM TmpFoo);
 +              RAISE INFO 'number of existing rows in TmpFoo - %', (SELECT count(*) FROM TmpFoo);
 +END ;
 +$$ LANGUAGE plpgsql;
 +SELECT TestJoinTempTable_CT();
 + testjointemptable_ct 
 +----------------------
 + 
 +(1 row)
 +
 +SELECT TestJoinTempTable_INSERT();
 +INFO:  number of existing rows in RealTable - 1000
 +INFO:  number of existing rows in TmpBar - 1000
 +INFO:  number of existing rows in TmpFoo - 0
 +INFO:  number of rows produced by query - 1000
 +INFO:  number of rows in TmpFoo - 1000
 +INFO:  number of existing rows in TmpFoo - 1000
 +INFO:  number of existing rows in TmpFoo - 1000
 + testjointemptable_insert 
 +--------------------------
 + 
 +(1 row)
 +
 +DROP TABLE RealTable;
 +DROP TABLE TmpBar;
 +DROP TABLE TmpFoo;
 +CREATE OR REPLACE FUNCTION TestJoinTempTable()
 +RETURNS void AS $$
 +BEGIN
 +        CREATE TABLE IF NOT EXISTS RealTable(ProductId int, ScenarioId int);
 +        TRUNCATE TABLE RealTable;
 +
 +        CREATE TEMPORARY TABLE IF NOT EXISTS TmpBar(NodeId int)
 +                DISTRIBUTE BY REPLICATION;
 +        CREATE TEMPORARY TABLE IF NOT EXISTS TmpFoo(TempId int)
 +                DISTRIBUTE BY REPLICATION;
 +
 +        INSERT INTO RealTable(ProductId, ScenarioId)
 +                SELECT generate_series(1,1000) as ProductId, (random() * 100)::int as ScenarioId;
 +
 +        INSERT INTO TmpBar(NodeId)
 +                SELECT generate_series(1,1000);
 +
 +        INSERT INTO TmpFoo(TempId)
 +               SELECT DISTINCT(PR.ProductId)
 +                 FROM RealTable AS PR
 +                 JOIN TmpBar tmp1 ON PR.ProductId = tmp1.NodeId;
 +END ;
 +$$ LANGUAGE plpgsql;
 +SELECT TestJoinTempTable();
 + testjointemptable 
 +-------------------
 + 
 +(1 row)
 +
 +-- Multiple invokations of the function showed interesting issues with command
 +-- passdown. So add that to the test case
 +SELECT TestJoinTempTable();
 +NOTICE:  relation "realtable" already exists, skipping
 +CONTEXT:  SQL statement "CREATE TABLE IF NOT EXISTS RealTable(ProductId int, ScenarioId int)"
 +PL/pgSQL function testjointemptable() line 3 at SQL statement
 +NOTICE:  relation "tmpbar" already exists, skipping
 +CONTEXT:  SQL statement "CREATE TEMPORARY TABLE IF NOT EXISTS TmpBar(NodeId int)
 +                DISTRIBUTE BY REPLICATION"
 +PL/pgSQL function testjointemptable() line 6 at SQL statement
 +NOTICE:  relation "tmpfoo" already exists, skipping
 +CONTEXT:  SQL statement "CREATE TEMPORARY TABLE IF NOT EXISTS TmpFoo(TempId int)
 +                DISTRIBUTE BY REPLICATION"
 +PL/pgSQL function testjointemptable() line 8 at SQL statement
 + testjointemptable 
 +-------------------
 + 
 +(1 row)
 +
 +SELECT TestJoinTempTable();
 +NOTICE:  relation "realtable" already exists, skipping
 +CONTEXT:  SQL statement "CREATE TABLE IF NOT EXISTS RealTable(ProductId int, ScenarioId int)"
 +PL/pgSQL function testjointemptable() line 3 at SQL statement
 +NOTICE:  relation "tmpbar" already exists, skipping
 +CONTEXT:  SQL statement "CREATE TEMPORARY TABLE IF NOT EXISTS TmpBar(NodeId int)
 +                DISTRIBUTE BY REPLICATION"
 +PL/pgSQL function testjointemptable() line 6 at SQL statement
 +NOTICE:  relation "tmpfoo" already exists, skipping
 +CONTEXT:  SQL statement "CREATE TEMPORARY TABLE IF NOT EXISTS TmpFoo(TempId int)
 +                DISTRIBUTE BY REPLICATION"
 +PL/pgSQL function testjointemptable() line 8 at SQL statement
 + testjointemptable 
 +-------------------
 + 
 +(1 row)
 +
 +DROP TABLE RealTable;
 +DROP TABLE TmpBar;
 +DROP TABLE TmpFoo;
++--
++-- Test handling of expanded arrays
++--
++create function returns_rw_array(int) returns int[]
++language plpgsql as $$
++  declare r int[];
++  begin r := array[$1, $1]; return r; end;
++$$ stable;
++create function consumes_rw_array(int[]) returns int
++language plpgsql as $$
++  begin return $1[1]; end;
++$$ stable;
++-- bug #14174
++explain (verbose, costs off)
++select i, a from
++  (select returns_rw_array(1) as a offset 0) ss,
++  lateral consumes_rw_array(a) i;
++                           QUERY PLAN                            
++-----------------------------------------------------------------
++ Nested Loop
++   Output: i.i, (returns_rw_array(1))
++   ->  Result
++         Output: returns_rw_array(1)
++   ->  Function Scan on public.consumes_rw_array i
++         Output: i.i
++         Function Call: consumes_rw_array((returns_rw_array(1)))
++(7 rows)
++
++select i, a from
++  (select returns_rw_array(1) as a offset 0) ss,
++  lateral consumes_rw_array(a) i;
++ i |   a   
++---+-------
++ 1 | {1,1}
++(1 row)
++
++explain (verbose, costs off)
++select consumes_rw_array(a), a from returns_rw_array(1) a;
++                 QUERY PLAN                 
++--------------------------------------------
++ Function Scan on public.returns_rw_array a
++   Output: consumes_rw_array(a), a
++   Function Call: returns_rw_array(1)
++(3 rows)
++
++select consumes_rw_array(a), a from returns_rw_array(1) a;
++ consumes_rw_array |   a   
++-------------------+-------
++                 1 | {1,1}
++(1 row)
++
++explain (verbose, costs off)
++select consumes_rw_array(a), a from
++  (values (returns_rw_array(1)), (returns_rw_array(2))) v(a);
++                             QUERY PLAN                              
++---------------------------------------------------------------------
++ Values Scan on "*VALUES*"
++   Output: consumes_rw_array("*VALUES*".column1), "*VALUES*".column1
++(2 rows)
++
++select consumes_rw_array(a), a from
++  (values (returns_rw_array(1)), (returns_rw_array(2))) v(a);
++ consumes_rw_array |   a   
++-------------------+-------
++                 1 | {1,1}
++                 2 | {2,2}
++(2 rows)
++
++--
++-- Test access to call stack
++--
 +create function inner_func(int)
 +returns int as $$
 +declare _context text;
 +begin
 +  get diagnostics _context = pg_context;
 +  raise notice '***%***', _context;
 +  -- lets do it again, just for fun..
 +  get diagnostics _context = pg_context;
 +  raise notice '***%***', _context;
 +  raise notice 'lets make sure we didnt break anything';
 +  return 2 * $1;
 +end;
 +$$ language plpgsql;
 +create or replace function outer_func(int)
 +returns int as $$
 +declare
 +  myresult int;
 +begin
 +  raise notice 'calling down into inner_func()';
 +  myresult := inner_func($1);
 +  raise notice 'inner_func() done';
 +  return myresult;
 +end;
 +$$ language plpgsql;
 +create or replace function outer_outer_func(int)
 +returns int as $$
 +declare
 +  myresult int;
 +begin
 +  raise notice 'calling down into outer_func()';
 +  myresult := outer_func($1);
 +  raise notice 'outer_func() done';
 +  return myresult;
 +end;
 +$$ language plpgsql;
 +select outer_outer_func(10);
 +NOTICE:  calling down into outer_func()
 +NOTICE:  calling down into inner_func()
 +CONTEXT:  PL/pgSQL function outer_outer_func(integer) line 6 at assignment
 +NOTICE:  ***PL/pgSQL function inner_func(integer) line 4 at GET DIAGNOSTICS
 +PL/pgSQL function outer_func(integer) line 6 at assignment
 +PL/pgSQL function outer_outer_func(integer) line 6 at assignment***
 +CONTEXT:  PL/pgSQL function outer_func(integer) line 6 at assignment
 +PL/pgSQL function outer_outer_func(integer) line 6 at assignment
 +NOTICE:  ***PL/pgSQL function inner_func(integer) line 7 at GET DIAGNOSTICS
 +PL/pgSQL function outer_func(integer) line 6 at assignment
 +PL/pgSQL function outer_outer_func(integer) line 6 at assignment***
 +CONTEXT:  PL/pgSQL function outer_func(integer) line 6 at assignment
 +PL/pgSQL function outer_outer_func(integer) line 6 at assignment
 +NOTICE:  lets make sure we didnt break anything
 +CONTEXT:  PL/pgSQL function outer_func(integer) line 6 at assignment
 +PL/pgSQL function outer_outer_func(integer) line 6 at assignment
 +NOTICE:  inner_func() done
 +CONTEXT:  PL/pgSQL function outer_outer_func(integer) line 6 at assignment
 +NOTICE:  outer_func() done
 + outer_outer_func 
 +------------------
 +               20
 +(1 row)
 +
 +-- repeated call should to work
 +select outer_outer_func(20);
 +NOTICE:  calling down into outer_func()
 +NOTICE:  calling down into inner_func()
 +CONTEXT:  PL/pgSQL function outer_outer_func(integer) line 6 at assignment
 +NOTICE:  ***PL/pgSQL function inner_func(integer) line 4 at GET DIAGNOSTICS
 +PL/pgSQL function outer_func(integer) line 6 at assignment
 +PL/pgSQL function outer_outer_func(integer) line 6 at assignment***
 +CONTEXT:  PL/pgSQL function outer_func(integer) line 6 at assignment
 +PL/pgSQL function outer_outer_func(integer) line 6 at assignment
 +NOTICE:  ***PL/pgSQL function inner_func(integer) line 7 at GET DIAGNOSTICS
 +PL/pgSQL function outer_func(integer) line 6 at assignment
 +PL/pgSQL function outer_outer_func(integer) line 6 at assignment***
 +CONTEXT:  PL/pgSQL function outer_func(integer) line 6 at assignment
 +PL/pgSQL function outer_outer_func(integer) line 6 at assignment
 +NOTICE:  lets make sure we didnt break anything
 +CONTEXT:  PL/pgSQL function outer_func(integer) line 6 at assignment
 +PL/pgSQL function outer_outer_func(integer) line 6 at assignment
 +NOTICE:  inner_func() done
 +CONTEXT:  PL/pgSQL function outer_outer_func(integer) line 6 at assignment
 +NOTICE:  outer_func() done
 + outer_outer_func 
 +------------------
 +               40
 +(1 row)
 +
 +drop function outer_outer_func(int);
 +drop function outer_func(int);
 +drop function inner_func(int);
 +-- access to call stack from exception
 +create function inner_func(int)
 +returns int as $$
 +declare
 +  _context text;
 +  sx int := 5;
 +begin
 +  begin
 +    perform sx / 0;
 +  exception
 +    when division_by_zero then
 +      get diagnostics _context = pg_context;
 +      raise notice '***%***', _context;
 +  end;
 +
 +  -- lets do it again, just for fun..
 +  get diagnostics _context = pg_context;
 +  raise notice '***%***', _context;
 +  raise notice 'lets make sure we didnt break anything';
 +  return 2 * $1;
 +end;
 +$$ language plpgsql;
 +create or replace function outer_func(int)
 +returns int as $$
 +declare
 +  myresult int;
 +begin
 +  raise notice 'calling down into inner_func()';
 +  myresult := inner_func($1);
 +  raise notice 'inner_func() done';
 +  return myresult;
 +end;
 +$$ language plpgsql;
 +create or replace function outer_outer_func(int)
 +returns int as $$
 +declare
 +  myresult int;
 +begin
 +  raise notice 'calling down into outer_func()';
 +  myresult := outer_func($1);
 +  raise notice 'outer_func() done';
 +  return myresult;
 +end;
 +$$ language plpgsql;
 +select outer_outer_func(10);
 +NOTICE:  calling down into outer_func()
 +NOTICE:  calling down into inner_func()
 +CONTEXT:  PL/pgSQL function outer_outer_func(integer) line 6 at assignment
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function inner_func(integer) line 6 during statement block entry
 +PL/pgSQL function outer_func(integer) line 6 at assignment
 +PL/pgSQL function outer_outer_func(integer) line 6 at assignment
 +-- repeated call should to work
 +select outer_outer_func(20);
 +NOTICE:  calling down into outer_func()
 +NOTICE:  calling down into inner_func()
 +CONTEXT:  PL/pgSQL function outer_outer_func(integer) line 6 at assignment
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function inner_func(integer) line 6 during statement block entry
 +PL/pgSQL function outer_func(integer) line 6 at assignment
 +PL/pgSQL function outer_outer_func(integer) line 6 at assignment
 +drop function outer_outer_func(int);
 +drop function outer_func(int);
 +drop function inner_func(int);
 +--
 +-- Test ASSERT
 +--
 +do $$
 +begin
 +  assert 1=1;  -- should succeed
 +end;
 +$$;
 +do $$
 +begin
 +  assert 1=0;  -- should fail
 +end;
 +$$;
 +ERROR:  assertion failed
 +CONTEXT:  PL/pgSQL function inline_code_block line 3 at ASSERT
 +do $$
 +begin
 +  assert NULL;  -- should fail
 +end;
 +$$;
 +ERROR:  assertion failed
 +CONTEXT:  PL/pgSQL function inline_code_block line 3 at ASSERT
 +-- check controlling GUC
 +set plpgsql.check_asserts = off;
 +do $$
 +begin
 +  assert 1=0;  -- won't be tested
 +end;
 +$$;
 +reset plpgsql.check_asserts;
 +-- test custom message
 +do $$
 +declare var text := 'some value';
 +begin
 +  assert 1=0, format('assertion failed, var = "%s"', var);
 +end;
 +$$;
 +ERROR:  assertion failed, var = "some value"
 +CONTEXT:  PL/pgSQL function inline_code_block line 4 at ASSERT
 +-- ensure assertions are not trapped by 'others'
 +do $$
 +begin
 +  assert 1=0, 'unhandled assertion';
 +exception when others then
 +  null; -- do nothing
 +end;
 +$$;
 +ERROR:  Internal subtransactions not supported in Postgres-XL
 +CONTEXT:  PL/pgSQL function inline_code_block line 2 during statement block entry
index 626a6f6edf00612094d489da2896c519f3401523,9931fd59449ca1347f6b44c0c5e6a29f7960da43..f4e7c4964d798cf27771d24cef1bb9f80cd60f25
@@@ -645,12 -664,23 +645,12 @@@ EXPLAIN (COSTS OFF) SELECT a, b, oid FR
  RESET SESSION AUTHORIZATION;
  SET row_security TO OFF;
  SELECT * FROM t1 WHERE f_leak(b);
 -NOTICE:  f_leak => aba
 -NOTICE:  f_leak => bbb
 -NOTICE:  f_leak => ccc
 -NOTICE:  f_leak => dad
 -NOTICE:  f_leak => abc
 -NOTICE:  f_leak => bcd
 -NOTICE:  f_leak => cde
 -NOTICE:  f_leak => def
 -NOTICE:  f_leak => xxx
 -NOTICE:  f_leak => yyy
 -NOTICE:  f_leak => zzz
   a |  b  
  ---+-----
-  1 | aaa
+  1 | aba
   2 | bbb
   3 | ccc
-  4 | ddd
+  4 | dad
   1 | abc
   2 | bcd
   3 | cde
@@@ -679,12 -706,23 +679,12 @@@ EXPLAIN (COSTS OFF) SELECT * FROM t1 WH
  SET SESSION AUTHORIZATION rls_regress_exempt_user;
  SET row_security TO OFF;
  SELECT * FROM t1 WHERE f_leak(b);
 -NOTICE:  f_leak => aba
 -NOTICE:  f_leak => bbb
 -NOTICE:  f_leak => ccc
 -NOTICE:  f_leak => dad
 -NOTICE:  f_leak => abc
 -NOTICE:  f_leak => bcd
 -NOTICE:  f_leak => cde
 -NOTICE:  f_leak => def
 -NOTICE:  f_leak => xxx
 -NOTICE:  f_leak => yyy
 -NOTICE:  f_leak => zzz
   a |  b  
  ---+-----
-  1 | aaa
+  1 | aba
   2 | bbb
   3 | ccc
-  4 | ddd
+  4 | dad
   1 | abc
   2 | bcd
   3 | cde
@@@ -928,12 -952,23 +928,12 @@@ EXPLAIN (COSTS OFF) EXECUTE p1(2)
  RESET SESSION AUTHORIZATION;
  SET row_security TO OFF;
  SELECT * FROM t1 WHERE f_leak(b);
 -NOTICE:  f_leak => aba
 -NOTICE:  f_leak => bbb
 -NOTICE:  f_leak => ccc
 -NOTICE:  f_leak => dad
 -NOTICE:  f_leak => abc
 -NOTICE:  f_leak => bcd
 -NOTICE:  f_leak => cde
 -NOTICE:  f_leak => def
 -NOTICE:  f_leak => xxx
 -NOTICE:  f_leak => yyy
 -NOTICE:  f_leak => zzz
   a |  b  
  ---+-----
-  1 | aaa
+  1 | aba
   2 | bbb
   3 | ccc
-  4 | ddd
+  4 | dad
   1 | abc
   2 | bcd
   3 | cde
@@@ -1090,20 -1120,30 +1090,20 @@@ UPDATE t1 SET b = b WHERE f_leak(b) RET
   a |      b      
  ---+-------------
   2 | bbbbbb_updt
 - 4 | daddad_updt
   2 | bcdbcd
 - 4 | defdef
   2 | yyyyyy
-  4 | dddddd_updt
++ 4 | daddad_updt
 + 4 | defdef
  (5 rows)
  
  UPDATE t1 SET b = b WHERE f_leak(b) RETURNING oid, *, t1;
   oid | a |      b      |       t1        
  -----+---+-------------+-----------------
   102 | 2 | bbbbbb_updt | (2,bbbbbb_updt)
 - 104 | 4 | daddad_updt | (4,daddad_updt)
   202 | 2 | bcdbcd      | (2,bcdbcd)
 - 204 | 4 | defdef      | (4,defdef)
   302 | 2 | yyyyyy      | (2,yyyyyy)
-  104 | 4 | dddddd_updt | (4,dddddd_updt)
++ 104 | 4 | daddad_updt | (4,daddad_updt)
 + 204 | 4 | defdef      | (4,defdef)
  (5 rows)
  
  -- updates with from clause
@@@ -1590,9 -1777,11 +1590,9 @@@ EXPLAIN (COSTS OFF) EXECUTE plancache_t
  
  SET SESSION AUTHORIZATION rls_regress_user2;
  SELECT * FROM z1 WHERE f_leak(b);
 -NOTICE:  f_leak => aba
 -NOTICE:  f_leak => ccc
   a |  b  
  ---+-----
-  1 | aaa
+  1 | aba
   3 | ccc
  (2 rows)
  
@@@ -1650,9 -1833,11 +1650,9 @@@ EXPLAIN (COSTS OFF) EXECUTE plancache_t
  
  SET ROLE rls_regress_group2;
  SELECT * FROM z1 WHERE f_leak(b);
 -NOTICE:  f_leak => aba
 -NOTICE:  f_leak => ccc
   a |  b  
  ---+-----
-  1 | aaa
+  1 | aba
   3 | ccc
  (2 rows)
  
@@@ -1718,12 -1897,16 +1718,12 @@@ GRANT SELECT ON rls_view TO rls_regress
  -- Query as role that is not owner of view or table.  Should return all records.
  SET SESSION AUTHORIZATION rls_regress_user1;
  SELECT * FROM rls_view;
 -NOTICE:  f_leak => aba
 -NOTICE:  f_leak => bbb
 -NOTICE:  f_leak => ccc
 -NOTICE:  f_leak => dad
   a |  b  
  ---+-----
-  1 | aaa
+  1 | aba
   2 | bbb
   3 | ccc
-  4 | ddd
+  4 | dad
  (4 rows)
  
  EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
  -- Query as view/table owner.  Should return all records.
  SET SESSION AUTHORIZATION rls_regress_user0;
  SELECT * FROM rls_view;
 -NOTICE:  f_leak => aba
 -NOTICE:  f_leak => bbb
 -NOTICE:  f_leak => ccc
 -NOTICE:  f_leak => dad
   a |  b  
  ---+-----
-  1 | aaa
+  1 | aba
   2 | bbb
   3 | ccc
-  4 | ddd
+  4 | dad
  (4 rows)
  
  EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
index 0ba5b716e3c53854d00fbd0a1eeb2f62e551c43c,6be8129f831c836753e14c6aadac6ba5635a9797..8795525c7546db47c1113a6eb88abaf007f5efbc
@@@ -50,6 -50,44 +50,44 @@@ DETAIL:  drop cascades to table selinto
  drop cascades to table selinto_schema.tmp2
  drop cascades to table selinto_schema.tmp3
  DROP USER selinto_user;
 -ERROR:  too many column names were specified
+ -- Tests for WITH NO DATA and column name consistency
+ CREATE TABLE ctas_base (i int, j int);
+ INSERT INTO ctas_base VALUES (1, 2);
+ CREATE TABLE ctas_nodata (ii, jj, kk) AS SELECT i, j FROM ctas_base; -- Error
 -ERROR:  too many column names were specified
++ERROR:  CREATE TABLE AS specifies too many column names
+ CREATE TABLE ctas_nodata (ii, jj, kk) AS SELECT i, j FROM ctas_base WITH NO DATA; -- Error
++ERROR:  CREATE TABLE AS specifies too many column names
+ CREATE TABLE ctas_nodata (ii, jj) AS SELECT i, j FROM ctas_base; -- OK
+ CREATE TABLE ctas_nodata_2 (ii, jj) AS SELECT i, j FROM ctas_base WITH NO DATA; -- OK
+ CREATE TABLE ctas_nodata_3 (ii) AS SELECT i, j FROM ctas_base; -- OK
+ CREATE TABLE ctas_nodata_4 (ii) AS SELECT i, j FROM ctas_base WITH NO DATA; -- OK
+ SELECT * FROM ctas_nodata;
+  ii | jj 
+ ----+----
+   1 |  2
+ (1 row)
+ SELECT * FROM ctas_nodata_2;
+  ii | jj 
+ ----+----
+ (0 rows)
+ SELECT * FROM ctas_nodata_3;
+  ii | j 
+ ----+---
+   1 | 2
+ (1 row)
+ SELECT * FROM ctas_nodata_4;
+  ii | j 
+ ----+---
+ (0 rows)
+ DROP TABLE ctas_base;
+ DROP TABLE ctas_nodata;
+ DROP TABLE ctas_nodata_2;
+ DROP TABLE ctas_nodata_3;
+ DROP TABLE ctas_nodata_4;
  --
  -- CREATE TABLE AS/SELECT INTO as last command in a SQL function
  -- have been known to cause problems
index c311e8151bce2d2674dfb48108328248ba1d5613,b2377e46109a6ccbf22659a976bae64b7c4f2baf..ed76878217fa8b573588c274afe7cce83e07a2da
@@@ -162,8 -154,46 +162,46 @@@ UPDATE CASE_TB
    FROM CASE2_TBL b
    WHERE j = -CASE_TBL.i;
  
 -SELECT * FROM CASE_TBL;
 +SELECT * FROM CASE_TBL ORDER BY i, f;
  
+ --
+ -- Nested CASE expressions
+ --
+ -- This test exercises a bug caused by aliasing econtext->caseValue_isNull
+ -- with the isNull argument of the inner CASE's ExecEvalCase() call.  After
+ -- evaluating the vol(null) expression in the inner CASE's second WHEN-clause,
+ -- the isNull flag for the case test value incorrectly became true, causing
+ -- the third WHEN-clause not to match.  The volatile function calls are needed
+ -- to prevent constant-folding in the planner, which would hide the bug.
+ CREATE FUNCTION vol(text) returns text as
+   'begin return $1; end' language plpgsql volatile;
+ SELECT CASE
+   (CASE vol('bar')
+     WHEN 'foo' THEN 'it was foo!'
+     WHEN vol(null) THEN 'null input'
+     WHEN 'bar' THEN 'it was bar!' END
+   )
+   WHEN 'it was foo!' THEN 'foo recognized'
+   WHEN 'it was bar!' THEN 'bar recognized'
+   ELSE 'unrecognized' END;
+ -- In this case, we can't inline the SQL function without confusing things.
+ CREATE DOMAIN foodomain AS text;
+ CREATE FUNCTION volfoo(text) returns foodomain as
+   'begin return $1::foodomain; end' language plpgsql volatile;
+ CREATE FUNCTION inline_eq(foodomain, foodomain) returns boolean as
+   'SELECT CASE $2::text WHEN $1::text THEN true ELSE false END' language sql;
+ CREATE OPERATOR = (procedure = inline_eq,
+                    leftarg = foodomain, rightarg = foodomain);
+ SELECT CASE volfoo('bar') WHEN 'foo'::foodomain THEN 'is foo' ELSE 'is not foo' END;
  --
  -- Clean up
  --
index e29e6aecc58c98025a373a48cceff817850a24aa,190c6055623166dbfd6a0200f59fb36d7c731a8f..2aba222966fbf83f1247905b493b118612311620
@@@ -263,6 -261,28 +263,28 @@@ insert into syscolconflicttest values (
  insert into syscolconflicttest values (1) on conflict (key) do update set data = excluded.oid::text;
  drop table syscolconflicttest;
  
 -create table insertconflict (a bigint, b bigint);
+ --
+ -- Previous tests all managed to not test any expressions requiring
+ -- planner preprocessing ...
+ --
++create table insertconflict (a bigint, b bigint) distribute by replication;
+ create unique index insertconflicti1 on insertconflict(coalesce(a, 0));
+ create unique index insertconflicti2 on insertconflict(b)
+   where coalesce(a, 1) > 0;
+ insert into insertconflict values (1, 2)
+ on conflict (coalesce(a, 0)) do nothing;
+ insert into insertconflict values (1, 2)
+ on conflict (b) where coalesce(a, 1) > 0 do nothing;
+ insert into insertconflict values (1, 2)
+ on conflict (b) where coalesce(a, 1) > 1 do nothing;
+ drop table insertconflict;
  
  -- ******************************************************************
  -- *                                                                *
index 2ba84e924442ac9a3fc1b178f2dc3c7cf87000fd,a37bf5ac5b36d4e88ccff9a535bb2f7771a492c4..ec66be15a9fa08e145356b41967065f18175f3fe
@@@ -170,17 -173,36 +170,35 @@@ UPDATE boxes SET b = '(2,2),(1,1)' WHER
  SELECT * FROM boxmv ORDER BY id;
  DROP TABLE boxes CASCADE;
  
---- make sure that column names are handled correctly
- CREATE TABLE v (i int, j int);
- CREATE MATERIALIZED VIEW mv_v (ii) AS SELECT i, j AS jj FROM v;
- ALTER TABLE v RENAME COLUMN i TO x;
- INSERT INTO v values (1, 2);
- CREATE UNIQUE INDEX mv_v_ii ON mv_v (ii);
- REFRESH MATERIALIZED VIEW mv_v;
- UPDATE v SET j = 3 WHERE x = 1;
- SELECT * FROM v;
- SELECT * FROM mv_v;
- DROP TABLE v CASCADE;
++
+ CREATE TABLE mvtest_v (i int, j int);
+ CREATE MATERIALIZED VIEW mvtest_mv_v (ii, jj, kk) AS SELECT i, j FROM mvtest_v; -- error
+ CREATE MATERIALIZED VIEW mvtest_mv_v (ii, jj) AS SELECT i, j FROM mvtest_v; -- ok
+ CREATE MATERIALIZED VIEW mvtest_mv_v_2 (ii) AS SELECT i, j FROM mvtest_v; -- ok
+ CREATE MATERIALIZED VIEW mvtest_mv_v_3 (ii, jj, kk) AS SELECT i, j FROM mvtest_v WITH NO DATA; -- error
+ CREATE MATERIALIZED VIEW mvtest_mv_v_3 (ii, jj) AS SELECT i, j FROM mvtest_v WITH NO DATA; -- ok
+ CREATE MATERIALIZED VIEW mvtest_mv_v_4 (ii) AS SELECT i, j FROM mvtest_v WITH NO DATA; -- ok
+ ALTER TABLE mvtest_v RENAME COLUMN i TO x;
+ INSERT INTO mvtest_v values (1, 2);
+ CREATE UNIQUE INDEX mvtest_mv_v_ii ON mvtest_mv_v (ii);
+ REFRESH MATERIALIZED VIEW mvtest_mv_v;
+ UPDATE mvtest_v SET j = 3 WHERE x = 1;
 -REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv_v;
++-- REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv_v;
+ REFRESH MATERIALIZED VIEW mvtest_mv_v_2;
+ REFRESH MATERIALIZED VIEW mvtest_mv_v_3;
+ REFRESH MATERIALIZED VIEW mvtest_mv_v_4;
+ SELECT * FROM mvtest_v;
+ SELECT * FROM mvtest_mv_v;
+ SELECT * FROM mvtest_mv_v_2;
+ SELECT * FROM mvtest_mv_v_3;
+ SELECT * FROM mvtest_mv_v_4;
+ DROP TABLE mvtest_v CASCADE;
+ -- make sure that create WITH NO DATA does not plan the query (bug #13907)
+ create materialized view mvtest_error as select 1/0 as x;  -- fail
+ create materialized view mvtest_error as select 1/0 as x with no data;
+ refresh materialized view mvtest_error;  -- fail here
+ drop materialized view mvtest_error;
  
  -- make sure that matview rows can be referenced as source rows (bug #9398)
  CREATE TABLE v AS SELECT generate_series(1,10) AS a;
index 94a0c2ce5e7754ae6d29cebb7251bcd73560f4f8,a375394bbc2dac6b62dce323b14ca5f9949bd25b..f41ee0e4da74584bf89c07db41779e851b5f6ace
@@@ -4204,90 -4199,49 +4204,131 @@@ select testoa(1,2,1); -- fail at updat
  drop function arrayassign1();
  drop function testoa(x1 int, x2 int, x3 int);
  
 +-- Check that DMLs in a plpgsql function work OK, when subsequent queries need
 +-- to open new datanode connections
 +CREATE OR REPLACE FUNCTION TestJoinTempTable_CT()
 +RETURNS void AS $$
 +BEGIN
 +        CREATE TABLE IF NOT EXISTS RealTable(ProductId int, ScenarioId int);
 +        TRUNCATE TABLE RealTable;
 +
 +        CREATE TABLE IF NOT EXISTS TmpBar(NodeId int)
 +                DISTRIBUTE BY REPLICATION;
 +        CREATE TABLE IF NOT EXISTS TmpFoo(TempId int)
 +                DISTRIBUTE BY REPLICATION;
 +END ;
 +$$ LANGUAGE plpgsql;
 +
 +CREATE OR REPLACE FUNCTION TestJoinTempTable_INSERT()
 +RETURNS void AS $$
 +BEGIN
 +        INSERT INTO RealTable(ProductId, ScenarioId)
 +                SELECT generate_series(1,1000) as ProductId, (random() * 100)::int as ScenarioId;
 +
 +        INSERT INTO TmpBar(NodeId)
 +                SELECT generate_series(1,1000);
 +              RAISE INFO 'number of existing rows in RealTable - %', (SELECT count(*) FROM RealTable);
 +              RAISE INFO 'number of existing rows in TmpBar - %', (SELECT count(*) FROM TmpBar);
 +              RAISE INFO 'number of existing rows in TmpFoo - %', (SELECT count(*) FROM TmpFoo);
 +        INSERT INTO TmpFoo(TempId)
 +               SELECT DISTINCT(PR.ProductId)
 +                 FROM RealTable AS PR
 +                 JOIN TmpBar tmp1 ON PR.ProductId = tmp1.NodeId;
 +
 +              RAISE INFO 'number of rows produced by query - %',
 +                              (SELECT COUNT(DISTINCT(PR.ProductId))
 +                               FROM RealTable AS PR
 +                 JOIN TmpBar tmp1 ON PR.ProductId = tmp1.NodeId);
 +              RAISE INFO 'number of rows in TmpFoo - %', (SELECT count(*) FROM TmpFoo);
 +              RAISE INFO 'number of existing rows in TmpFoo - %', (SELECT count(*) FROM TmpFoo);
 +              RAISE INFO 'number of existing rows in TmpFoo - %', (SELECT count(*) FROM TmpFoo);
 +END ;
 +$$ LANGUAGE plpgsql;
 +
 +SELECT TestJoinTempTable_CT();
 +SELECT TestJoinTempTable_INSERT();
 +
 +DROP TABLE RealTable;
 +DROP TABLE TmpBar;
 +DROP TABLE TmpFoo;
 +
 +CREATE OR REPLACE FUNCTION TestJoinTempTable()
 +RETURNS void AS $$
 +BEGIN
 +        CREATE TABLE IF NOT EXISTS RealTable(ProductId int, ScenarioId int);
 +        TRUNCATE TABLE RealTable;
 +
 +        CREATE TEMPORARY TABLE IF NOT EXISTS TmpBar(NodeId int)
 +                DISTRIBUTE BY REPLICATION;
 +        CREATE TEMPORARY TABLE IF NOT EXISTS TmpFoo(TempId int)
 +                DISTRIBUTE BY REPLICATION;
 +
 +        INSERT INTO RealTable(ProductId, ScenarioId)
 +                SELECT generate_series(1,1000) as ProductId, (random() * 100)::int as ScenarioId;
 +
 +        INSERT INTO TmpBar(NodeId)
 +                SELECT generate_series(1,1000);
 +
 +        INSERT INTO TmpFoo(TempId)
 +               SELECT DISTINCT(PR.ProductId)
 +                 FROM RealTable AS PR
 +                 JOIN TmpBar tmp1 ON PR.ProductId = tmp1.NodeId;
 +END ;
 +$$ LANGUAGE plpgsql;
 +
 +SELECT TestJoinTempTable();
 +
 +-- Multiple invokations of the function showed interesting issues with command
 +-- passdown. So add that to the test case
 +SELECT TestJoinTempTable();
 +SELECT TestJoinTempTable();
 +
 +DROP TABLE RealTable;
 +DROP TABLE TmpBar;
 +DROP TABLE TmpFoo;
  
- -- access to call stack
+ --
+ -- Test handling of expanded arrays
+ --
+ create function returns_rw_array(int) returns int[]
+ language plpgsql as $$
+   declare r int[];
+   begin r := array[$1, $1]; return r; end;
+ $$ stable;
+ create function consumes_rw_array(int[]) returns int
+ language plpgsql as $$
+   begin return $1[1]; end;
+ $$ stable;
+ -- bug #14174
+ explain (verbose, costs off)
+ select i, a from
+   (select returns_rw_array(1) as a offset 0) ss,
+   lateral consumes_rw_array(a) i;
+ select i, a from
+   (select returns_rw_array(1) as a offset 0) ss,
+   lateral consumes_rw_array(a) i;
+ explain (verbose, costs off)
+ select consumes_rw_array(a), a from returns_rw_array(1) a;
+ select consumes_rw_array(a), a from returns_rw_array(1) a;
+ explain (verbose, costs off)
+ select consumes_rw_array(a), a from
+   (values (returns_rw_array(1)), (returns_rw_array(2))) v(a);
+ select consumes_rw_array(a), a from
+   (values (returns_rw_array(1)), (returns_rw_array(2))) v(a);
+ --
+ -- Test access to call stack
+ --
  create function inner_func(int)
  returns int as $$
  declare _context text;
Simple merge
Simple merge