Reducing opr_sanity test's runtime under CLOBBER_CACHE_ALWAYS - Mailing list pgsql-hackers

In a nearby thread I bemoaned the fact that the core regression tests
seem to have gotten significantly slower in the last couple of months,
at least with CCA enabled: hyrax reports completing them in 12:52:44
on 18 March, while its most recent run on 1 May took 14:08:18.

Trying to diagnose the cause overall seemed a bit daunting, but
I thought I'd dig into the opr_sanity test in particular, as it
is one of the slower tests under CCA to start with and had also
slowed down noticeably (from 3701581 ms to 4761183 ms, or 28%).
I was able to complete a bisection using just that test, and
got an unexpected result: most of the slowdown appeared at
ab596105b (BRIN minmax-multi indexes).  Apparently the additional
time is simply from having to check the additional pg_amop and
pg_amproc entries, which that patch added quite a few of.

I noticed that all of the slowest queries in that test were dependent
on the binary_coercible() plpgsql function that it uses.  Now, that
function has always been a rather lame attempt to approximate the
behavior of the parser's IsBinaryCoercible() function, so I've been
thinking for some time that we ought to get rid of it in favor of
actually using IsBinaryCoercible().  I tried that, by adding a
shim function to regress.c, and got a most gratifying result:
on my machine opr_sanity's runtime with
debug_invalidate_system_caches_always = 1 drops from
29m9s to 3m19s.  Without CCA the speedup is far less impressive,
360ms to 305ms, but that's still useful.  Especially since this
makes the test strictly more accurate.

(I am thinking that this suggests that plpgsql may be hurt more
by cache clobbers than it really needs to be; but doing anything
about that would require some research.)

Anyway, I propose that we ought to sneak this into HEAD, since
it's only touching test code and not anything production-critical.

The patch is a bit more invasive than I would have liked, because
adding the SQL definition of binary_coercible() to create_function_1
(where the other regress.c functions are declared) didn't work:
that runs after opr_sanity, and just moving it up to before
opr_sanity causes the latter to complain about some of the functions
in it.  So I ended up splitting the create_function_1 test into
create_function_0 and create_function_1.   It's annoying from a
parallelism standpoint that create_function_0 runs by itself, but
the two parallel groups ahead of it are already full.  Maybe we
should rebalance that by moving a few of those tests to run in
parallel with create_function_0, but I didn't do that here.

Thoughts?

            regards, tom lane

diff --git a/src/test/regress/expected/.gitignore b/src/test/regress/expected/.gitignore
index 93c56c85a0..b99caf5f40 100644
--- a/src/test/regress/expected/.gitignore
+++ b/src/test/regress/expected/.gitignore
@@ -1,5 +1,6 @@
 /constraints.out
 /copy.out
+/create_function_0.out
 /create_function_1.out
 /create_function_2.out
 /largeobject.out
diff --git a/src/test/regress/expected/conversion.out b/src/test/regress/expected/conversion.out
index e34ab20974..04fdcba496 100644
--- a/src/test/regress/expected/conversion.out
+++ b/src/test/regress/expected/conversion.out
@@ -41,7 +41,7 @@ DROP USER regress_conversion_user;
 -- Test built-in conversion functions.
 --
 -- Helper function to test a conversion. Uses the test_enc_conversion function
--- that was created in the create_function_1 test.
+-- that was created in the create_function_0 test.
 create or replace function test_conv(
   input IN bytea,
   src_encoding IN text,
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 7a0d345b60..562b586d8e 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -16,61 +16,6 @@
 --
 -- NB: run this test earlier than the create_operator test, because
 -- that test creates some bogus operators...
--- Helper functions to deal with cases where binary-coercible matches are
--- allowed.
--- This should match IsBinaryCoercible() in parse_coerce.c.
--- It doesn't currently know about some cases, notably domains, anyelement,
--- anynonarray, anyenum, or record, but it doesn't need to (yet).
-create function binary_coercible(oid, oid) returns bool as $$
-begin
-  if $1 = $2 then return true; end if;
-  if EXISTS(select 1 from pg_catalog.pg_cast where
-            castsource = $1 and casttarget = $2 and
-            castmethod = 'b' and castcontext = 'i')
-  then return true; end if;
-  if $2 = 'pg_catalog.any'::pg_catalog.regtype then return true; end if;
-  if $2 = 'pg_catalog.anyarray'::pg_catalog.regtype then
-    if EXISTS(select 1 from pg_catalog.pg_type where
-              oid = $1 and typelem != 0 and
-              typsubscript = 'pg_catalog.array_subscript_handler'::pg_catalog.regproc)
-    then return true; end if;
-  end if;
-  if $2 = 'pg_catalog.anyrange'::pg_catalog.regtype then
-    if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
-    then return true; end if;
-  end if;
-  if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
-    if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
-    then return true; end if;
-  end if;
-  return false;
-end
-$$ language plpgsql strict stable;
--- This one ignores castcontext, so it will allow cases where an explicit
--- (but still binary) cast would be required to convert the input type.
--- We don't currently use this for any tests in this file, but it is a
--- reasonable alternative definition for some scenarios.
-create function explicitly_binary_coercible(oid, oid) returns bool as $$
-begin
-  if $1 = $2 then return true; end if;
-  if EXISTS(select 1 from pg_catalog.pg_cast where
-            castsource = $1 and casttarget = $2 and
-            castmethod = 'b')
-  then return true; end if;
-  if $2 = 'pg_catalog.any'::pg_catalog.regtype then return true; end if;
-  if $2 = 'pg_catalog.anyarray'::pg_catalog.regtype then
-    if EXISTS(select 1 from pg_catalog.pg_type where
-              oid = $1 and typelem != 0 and
-              typsubscript = 'pg_catalog.array_subscript_handler'::pg_catalog.regproc)
-    then return true; end if;
-  end if;
-  if $2 = 'pg_catalog.anyrange'::pg_catalog.regtype then
-    if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
-    then return true; end if;
-  end if;
-  return false;
-end
-$$ language plpgsql strict stable;
 -- **************** pg_proc ****************
 -- Look for illegal values in pg_proc fields.
 SELECT p1.oid, p1.proname
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 5480f979c6..f567fd378e 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -635,7 +635,7 @@ WHERE (rngcollation = 0) != (typcollation = 0);
 (0 rows)

 -- opclass had better be a btree opclass accepting the subtype.
--- We must allow anyarray matches, cf opr_sanity's binary_coercible()
+-- We must allow anyarray matches, cf IsBinaryCoercible()
 SELECT p1.rngtypid, p1.rngsubtype, o.opcmethod, o.opcname
 FROM pg_range p1 JOIN pg_opclass o ON o.oid = p1.rngsubopc
 WHERE o.opcmethod != 403 OR
diff --git a/src/test/regress/input/create_function_0.source b/src/test/regress/input/create_function_0.source
new file mode 100644
index 0000000000..28fe373c7d
--- /dev/null
+++ b/src/test/regress/input/create_function_0.source
@@ -0,0 +1,42 @@
+--
+-- CREATE_FUNCTION_0
+--
+-- This makes available various test support functions that are in
+-- src/test/regress/regress.c
+-- (although a few more are created in create_function_1)
+--
+
+CREATE FUNCTION make_tuple_indirect (record)
+    RETURNS record
+    AS '@libdir@/regress@DLSUFFIX@'
+    LANGUAGE C STRICT;
+
+CREATE FUNCTION test_atomic_ops()
+    RETURNS bool
+    AS '@libdir@/regress@DLSUFFIX@'
+    LANGUAGE C;
+
+CREATE FUNCTION test_fdw_handler()
+    RETURNS fdw_handler
+    AS '@libdir@/regress@DLSUFFIX@', 'test_fdw_handler'
+    LANGUAGE C;
+
+CREATE FUNCTION test_support_func(internal)
+    RETURNS internal
+    AS '@libdir@/regress@DLSUFFIX@', 'test_support_func'
+    LANGUAGE C STRICT;
+
+CREATE FUNCTION test_opclass_options_func(internal)
+    RETURNS void
+    AS '@libdir@/regress@DLSUFFIX@', 'test_opclass_options_func'
+    LANGUAGE C;
+
+CREATE FUNCTION test_enc_conversion(bytea, name, name, bool,
+                                    validlen OUT int, result OUT bytea)
+    AS '@libdir@/regress@DLSUFFIX@', 'test_enc_conversion'
+    LANGUAGE C STRICT;
+
+CREATE FUNCTION binary_coercible(oid, oid)
+    RETURNS bool
+    AS '@libdir@/regress@DLSUFFIX@', 'binary_coercible'
+    LANGUAGE C STRICT STABLE PARALLEL SAFE;
diff --git a/src/test/regress/input/create_function_1.source b/src/test/regress/input/create_function_1.source
index 6c69b7fe6c..8b58860245 100644
--- a/src/test/regress/input/create_function_1.source
+++ b/src/test/regress/input/create_function_1.source
@@ -52,36 +52,6 @@ CREATE FUNCTION set_ttdummy (int4)
         AS '@libdir@/regress@DLSUFFIX@'
         LANGUAGE C STRICT;

-CREATE FUNCTION make_tuple_indirect (record)
-        RETURNS record
-        AS '@libdir@/regress@DLSUFFIX@'
-        LANGUAGE C STRICT;
-
-CREATE FUNCTION test_atomic_ops()
-    RETURNS bool
-    AS '@libdir@/regress@DLSUFFIX@'
-    LANGUAGE C;
-
--- Tests creating a FDW handler
-CREATE FUNCTION test_fdw_handler()
-    RETURNS fdw_handler
-    AS '@libdir@/regress@DLSUFFIX@', 'test_fdw_handler'
-    LANGUAGE C;
-
-CREATE FUNCTION test_support_func(internal)
-    RETURNS internal
-    AS '@libdir@/regress@DLSUFFIX@', 'test_support_func'
-    LANGUAGE C STRICT;
-
-CREATE FUNCTION test_opclass_options_func(internal)
-    RETURNS void
-    AS '@libdir@/regress@DLSUFFIX@', 'test_opclass_options_func'
-    LANGUAGE C;
-
-CREATE FUNCTION test_enc_conversion(bytea, name, name, bool, validlen OUT int, result OUT bytea)
-    AS '@libdir@/regress@DLSUFFIX@', 'test_enc_conversion'
-    LANGUAGE C STRICT;
-
 -- Things that shouldn't work:

 CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
diff --git a/src/test/regress/output/create_function_0.source b/src/test/regress/output/create_function_0.source
new file mode 100644
index 0000000000..a90049a760
--- /dev/null
+++ b/src/test/regress/output/create_function_0.source
@@ -0,0 +1,35 @@
+--
+-- CREATE_FUNCTION_0
+--
+-- This makes available various test support functions that are in
+-- src/test/regress/regress.c
+-- (although a few more are created in create_function_1)
+--
+CREATE FUNCTION make_tuple_indirect (record)
+    RETURNS record
+    AS '@libdir@/regress@DLSUFFIX@'
+    LANGUAGE C STRICT;
+CREATE FUNCTION test_atomic_ops()
+    RETURNS bool
+    AS '@libdir@/regress@DLSUFFIX@'
+    LANGUAGE C;
+CREATE FUNCTION test_fdw_handler()
+    RETURNS fdw_handler
+    AS '@libdir@/regress@DLSUFFIX@', 'test_fdw_handler'
+    LANGUAGE C;
+CREATE FUNCTION test_support_func(internal)
+    RETURNS internal
+    AS '@libdir@/regress@DLSUFFIX@', 'test_support_func'
+    LANGUAGE C STRICT;
+CREATE FUNCTION test_opclass_options_func(internal)
+    RETURNS void
+    AS '@libdir@/regress@DLSUFFIX@', 'test_opclass_options_func'
+    LANGUAGE C;
+CREATE FUNCTION test_enc_conversion(bytea, name, name, bool,
+                                    validlen OUT int, result OUT bytea)
+    AS '@libdir@/regress@DLSUFFIX@', 'test_enc_conversion'
+    LANGUAGE C STRICT;
+CREATE FUNCTION binary_coercible(oid, oid)
+    RETURNS bool
+    AS '@libdir@/regress@DLSUFFIX@', 'binary_coercible'
+    LANGUAGE C STRICT STABLE PARALLEL SAFE;
diff --git a/src/test/regress/output/create_function_1.source b/src/test/regress/output/create_function_1.source
index c66146db9d..88265779f7 100644
--- a/src/test/regress/output/create_function_1.source
+++ b/src/test/regress/output/create_function_1.source
@@ -47,30 +47,6 @@ CREATE FUNCTION set_ttdummy (int4)
         RETURNS int4
         AS '@libdir@/regress@DLSUFFIX@'
         LANGUAGE C STRICT;
-CREATE FUNCTION make_tuple_indirect (record)
-        RETURNS record
-        AS '@libdir@/regress@DLSUFFIX@'
-        LANGUAGE C STRICT;
-CREATE FUNCTION test_atomic_ops()
-    RETURNS bool
-    AS '@libdir@/regress@DLSUFFIX@'
-    LANGUAGE C;
--- Tests creating a FDW handler
-CREATE FUNCTION test_fdw_handler()
-    RETURNS fdw_handler
-    AS '@libdir@/regress@DLSUFFIX@', 'test_fdw_handler'
-    LANGUAGE C;
-CREATE FUNCTION test_support_func(internal)
-    RETURNS internal
-    AS '@libdir@/regress@DLSUFFIX@', 'test_support_func'
-    LANGUAGE C STRICT;
-CREATE FUNCTION test_opclass_options_func(internal)
-    RETURNS void
-    AS '@libdir@/regress@DLSUFFIX@', 'test_opclass_options_func'
-    LANGUAGE C;
-CREATE FUNCTION test_enc_conversion(bytea, name, name, bool, validlen OUT int, result OUT bytea)
-    AS '@libdir@/regress@DLSUFFIX@', 'test_enc_conversion'
-    LANGUAGE C STRICT;
 -- Things that shouldn't work:
 CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
     AS 'SELECT ''not an integer'';';
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index a091300857..45696031a7 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -24,6 +24,9 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
 # ----------
 test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet
macaddrmacaddr8 tstypes multirangetypes 

+# opr_sanity depends on this
+test: create_function_0
+
 # ----------
 # Another group of parallel tests
 # geometry depends on point, lseg, box, path, polygon and circle
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index 1990cbb6a1..d8756e5ba0 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -36,6 +36,7 @@
 #include "nodes/supportnodes.h"
 #include "optimizer/optimizer.h"
 #include "optimizer/plancat.h"
+#include "parser/parse_coerce.h"
 #include "port/atomics.h"
 #include "storage/spin.h"
 #include "utils/builtins.h"
@@ -1194,3 +1195,14 @@ test_enc_conversion(PG_FUNCTION_ARGS)

     PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
 }
+
+/* Provide SQL access to IsBinaryCoercible() */
+PG_FUNCTION_INFO_V1(binary_coercible);
+Datum
+binary_coercible(PG_FUNCTION_ARGS)
+{
+    Oid            srctype = PG_GETARG_OID(0);
+    Oid            targettype = PG_GETARG_OID(1);
+
+    PG_RETURN_BOOL(IsBinaryCoercible(srctype, targettype));
+}
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 5644847601..bdadb1db29 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -43,6 +43,7 @@ test: inet
 test: macaddr
 test: macaddr8
 test: tstypes
+test: create_function_0
 test: geometry
 test: horology
 test: regex
diff --git a/src/test/regress/sql/.gitignore b/src/test/regress/sql/.gitignore
index 46c8112094..fe14af6ae7 100644
--- a/src/test/regress/sql/.gitignore
+++ b/src/test/regress/sql/.gitignore
@@ -1,5 +1,6 @@
 /constraints.sql
 /copy.sql
+/create_function_0.sql
 /create_function_1.sql
 /create_function_2.sql
 /largeobject.sql
diff --git a/src/test/regress/sql/conversion.sql b/src/test/regress/sql/conversion.sql
index ea85f20ed8..8358682432 100644
--- a/src/test/regress/sql/conversion.sql
+++ b/src/test/regress/sql/conversion.sql
@@ -40,7 +40,7 @@ DROP USER regress_conversion_user;
 --

 -- Helper function to test a conversion. Uses the test_enc_conversion function
--- that was created in the create_function_1 test.
+-- that was created in the create_function_0 test.
 create or replace function test_conv(
   input IN bytea,
   src_encoding IN text,
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 393acdf8c3..5a9c479692 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -18,65 +18,6 @@
 -- that test creates some bogus operators...


--- Helper functions to deal with cases where binary-coercible matches are
--- allowed.
-
--- This should match IsBinaryCoercible() in parse_coerce.c.
--- It doesn't currently know about some cases, notably domains, anyelement,
--- anynonarray, anyenum, or record, but it doesn't need to (yet).
-create function binary_coercible(oid, oid) returns bool as $$
-begin
-  if $1 = $2 then return true; end if;
-  if EXISTS(select 1 from pg_catalog.pg_cast where
-            castsource = $1 and casttarget = $2 and
-            castmethod = 'b' and castcontext = 'i')
-  then return true; end if;
-  if $2 = 'pg_catalog.any'::pg_catalog.regtype then return true; end if;
-  if $2 = 'pg_catalog.anyarray'::pg_catalog.regtype then
-    if EXISTS(select 1 from pg_catalog.pg_type where
-              oid = $1 and typelem != 0 and
-              typsubscript = 'pg_catalog.array_subscript_handler'::pg_catalog.regproc)
-    then return true; end if;
-  end if;
-  if $2 = 'pg_catalog.anyrange'::pg_catalog.regtype then
-    if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
-    then return true; end if;
-  end if;
-  if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
-    if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
-    then return true; end if;
-  end if;
-  return false;
-end
-$$ language plpgsql strict stable;
-
--- This one ignores castcontext, so it will allow cases where an explicit
--- (but still binary) cast would be required to convert the input type.
--- We don't currently use this for any tests in this file, but it is a
--- reasonable alternative definition for some scenarios.
-create function explicitly_binary_coercible(oid, oid) returns bool as $$
-begin
-  if $1 = $2 then return true; end if;
-  if EXISTS(select 1 from pg_catalog.pg_cast where
-            castsource = $1 and casttarget = $2 and
-            castmethod = 'b')
-  then return true; end if;
-  if $2 = 'pg_catalog.any'::pg_catalog.regtype then return true; end if;
-  if $2 = 'pg_catalog.anyarray'::pg_catalog.regtype then
-    if EXISTS(select 1 from pg_catalog.pg_type where
-              oid = $1 and typelem != 0 and
-              typsubscript = 'pg_catalog.array_subscript_handler'::pg_catalog.regproc)
-    then return true; end if;
-  end if;
-  if $2 = 'pg_catalog.anyrange'::pg_catalog.regtype then
-    if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
-    then return true; end if;
-  end if;
-  return false;
-end
-$$ language plpgsql strict stable;
-
-
 -- **************** pg_proc ****************

 -- Look for illegal values in pg_proc fields.
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index 4739aca84a..404c3a2043 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -465,7 +465,7 @@ FROM pg_range p1 JOIN pg_type t ON t.oid = p1.rngsubtype
 WHERE (rngcollation = 0) != (typcollation = 0);

 -- opclass had better be a btree opclass accepting the subtype.
--- We must allow anyarray matches, cf opr_sanity's binary_coercible()
+-- We must allow anyarray matches, cf IsBinaryCoercible()

 SELECT p1.rngtypid, p1.rngsubtype, o.opcmethod, o.opcname
 FROM pg_range p1 JOIN pg_opclass o ON o.oid = p1.rngsubopc

pgsql-hackers by date:

Previous
From: Ahsan Hadi
Date:
Subject: Re: [BUG] Logical replication failure "ERROR: could not map filenode "base/13237/442428" to relation OID" with catalog modifying txns
Next
From: Tom Lane
Date:
Subject: Re: Will Postgres12 installed on a RHEL 6 server continue to function after the server get O/S upgrade to RHEL 7?