summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPeter Geoghegan2020-02-26 19:28:25 +0000
committerPeter Geoghegan2020-02-26 19:28:25 +0000
commit612a1ab76724aa1514b6509269342649f8cab375 (patch)
treedf13756515fd71d6528958f2315123d89d41b817 /src
parent4109bb5de4998b9301ea2ac18c9d6dfb0b4f900b (diff)
Add equalimage B-Tree support functions.
Invent the concept of a B-Tree equalimage ("equality implies image equality") support function, registered as support function 4. This indicates whether it is safe (or not safe) to apply optimizations that assume that any two datums considered equal by an operator class's order method must be interchangeable without any loss of semantic information. This is static information about an operator class and a collation. Register an equalimage routine for almost all of the existing B-Tree opclasses. We only need two trivial routines for all of the opclasses that are included with the core distribution. There is one routine for opclasses that index non-collatable types (which returns 'true' unconditionally), plus another routine for collatable types (which returns 'true' when the collation is a deterministic collation). This patch is infrastructure for an upcoming patch that adds B-Tree deduplication. Author: Peter Geoghegan, Anastasia Lubennikova Discussion: https://postgr.es/m/CAH2-Wzn3Ee49Gmxb7V1VJ3-AC8fWn-Fr8pfWQebHe8rYRxt5OQ@mail.gmail.com
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/nbtree/nbtutils.c73
-rw-r--r--src/backend/access/nbtree/nbtvalidate.c8
-rw-r--r--src/backend/commands/opclasscmds.c30
-rw-r--r--src/backend/utils/adt/datum.c26
-rw-r--r--src/backend/utils/adt/varlena.c20
-rw-r--r--src/bin/pg_dump/t/002_pg_dump.pl12
-rw-r--r--src/include/access/nbtree.h8
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_amproc.dat60
-rw-r--r--src/include/catalog/pg_proc.dat6
-rw-r--r--src/test/regress/expected/alter_generic.out8
-rw-r--r--src/test/regress/expected/opr_sanity.out36
-rw-r--r--src/test/regress/sql/alter_generic.sql3
-rw-r--r--src/test/regress/sql/opr_sanity.sql18
14 files changed, 297 insertions, 13 deletions
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 5ab4e712f12..af07732eabc 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -20,6 +20,7 @@
#include "access/nbtree.h"
#include "access/reloptions.h"
#include "access/relscan.h"
+#include "catalog/catalog.h"
#include "commands/progress.h"
#include "lib/qunique.h"
#include "miscadmin.h"
@@ -2566,3 +2567,75 @@ _bt_check_third_page(Relation rel, Relation heap, bool needheaptidspace,
"or use full text indexing."),
errtableconstraint(heap, RelationGetRelationName(rel))));
}
+
+/*
+ * Are all attributes in rel "equality is image equality" attributes?
+ *
+ * We use each attribute's BTEQUALIMAGE_PROC opclass procedure. If any
+ * opclass either lacks a BTEQUALIMAGE_PROC procedure or returns false, we
+ * return false; otherwise we return true.
+ *
+ * Returned boolean value is stored in index metapage during index builds.
+ * Deduplication can only be used when we return true.
+ */
+bool
+_bt_allequalimage(Relation rel, bool debugmessage)
+{
+ bool allequalimage = true;
+
+ /* INCLUDE indexes don't support deduplication */
+ if (IndexRelationGetNumberOfAttributes(rel) !=
+ IndexRelationGetNumberOfKeyAttributes(rel))
+ return false;
+
+ /*
+ * There is no special reason why deduplication cannot work with system
+ * relations (i.e. with system catalog indexes and TOAST indexes). We
+ * deem deduplication unsafe for these indexes all the same, since the
+ * alternative is to force users to always use deduplication, without
+ * being able to opt out. (ALTER INDEX is not supported with system
+ * indexes, so users would have no way to set the deduplicate_items
+ * storage parameter to 'off'.)
+ */
+ if (IsSystemRelation(rel))
+ return false;
+
+ for (int i = 0; i < IndexRelationGetNumberOfKeyAttributes(rel); i++)
+ {
+ Oid opfamily = rel->rd_opfamily[i];
+ Oid opcintype = rel->rd_opcintype[i];
+ Oid collation = rel->rd_indcollation[i];
+ Oid equalimageproc;
+
+ equalimageproc = get_opfamily_proc(opfamily, opcintype, opcintype,
+ BTEQUALIMAGE_PROC);
+
+ /*
+ * If there is no BTEQUALIMAGE_PROC then deduplication is assumed to
+ * be unsafe. Otherwise, actually call proc and see what it says.
+ */
+ if (!OidIsValid(equalimageproc) ||
+ !DatumGetBool(OidFunctionCall1Coll(equalimageproc, collation,
+ ObjectIdGetDatum(opcintype))))
+ {
+ allequalimage = false;
+ break;
+ }
+ }
+
+ /*
+ * Don't elog() until here to avoid reporting on a system relation index
+ * or an INCLUDE index
+ */
+ if (debugmessage)
+ {
+ if (allequalimage)
+ elog(DEBUG1, "index \"%s\" can safely use deduplication",
+ RelationGetRelationName(rel));
+ else
+ elog(DEBUG1, "index \"%s\" cannot use deduplication",
+ RelationGetRelationName(rel));
+ }
+
+ return allequalimage;
+}
diff --git a/src/backend/access/nbtree/nbtvalidate.c b/src/backend/access/nbtree/nbtvalidate.c
index ff634b16499..627f74407a3 100644
--- a/src/backend/access/nbtree/nbtvalidate.c
+++ b/src/backend/access/nbtree/nbtvalidate.c
@@ -104,6 +104,10 @@ btvalidate(Oid opclassoid)
procform->amprocrighttype,
BOOLOID, BOOLOID);
break;
+ case BTEQUALIMAGE_PROC:
+ ok = check_amproc_signature(procform->amproc, BOOLOID, true,
+ 1, 1, OIDOID);
+ break;
default:
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -211,8 +215,8 @@ btvalidate(Oid opclassoid)
/*
* Complain if there seems to be an incomplete set of either operators
- * or support functions for this datatype pair. The only things
- * considered optional are the sortsupport and in_range functions.
+ * or support functions for this datatype pair. The sortsupport,
+ * in_range, and equalimage functions are considered optional.
*/
if (thisgroup->operatorset !=
((1 << BTLessStrategyNumber) |
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index e2c6de457cf..743511bdf21 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -1143,9 +1143,10 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
/*
* btree comparison procs must be 2-arg procs returning int4. btree
* sortsupport procs must take internal and return void. btree in_range
- * procs must be 5-arg procs returning bool. hash support proc 1 must be
- * a 1-arg proc returning int4, while proc 2 must be a 2-arg proc
- * returning int8. Otherwise we don't know.
+ * procs must be 5-arg procs returning bool. btree equalimage procs must
+ * take 1 arg and return bool. hash support proc 1 must be a 1-arg proc
+ * returning int4, while proc 2 must be a 2-arg proc returning int8.
+ * Otherwise we don't know.
*/
if (amoid == BTREE_AM_OID)
{
@@ -1205,6 +1206,29 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
if (!OidIsValid(member->righttype))
member->righttype = procform->proargtypes.values[2];
}
+ else if (member->number == BTEQUALIMAGE_PROC)
+ {
+ if (procform->pronargs != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree equal image functions must have one argument")));
+ if (procform->prorettype != BOOLOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree equal image functions must return boolean")));
+ /*
+ * pg_amproc functions are indexed by (lefttype, righttype), but
+ * an equalimage function can only be called at CREATE INDEX time.
+ * The same opclass opcintype OID is always used for leftype and
+ * righttype. Providing a cross-type routine isn't sensible.
+ * Reject cross-type ALTER OPERATOR FAMILY ... ADD FUNCTION 4
+ * statements here.
+ */
+ if (member->lefttype != member->righttype)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree equal image functions must not be cross-type")));
+ }
}
else if (amoid == HASH_AM_OID)
{
diff --git a/src/backend/utils/adt/datum.c b/src/backend/utils/adt/datum.c
index 4e819473520..34cdde1bb91 100644
--- a/src/backend/utils/adt/datum.c
+++ b/src/backend/utils/adt/datum.c
@@ -44,6 +44,7 @@
#include "access/detoast.h"
#include "fmgr.h"
+#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/expandeddatum.h"
@@ -324,6 +325,31 @@ datum_image_eq(Datum value1, Datum value2, bool typByVal, int typLen)
}
/*-------------------------------------------------------------------------
+ * btequalimage
+ *
+ * Generic "equalimage" support function.
+ *
+ * B-Tree operator classes whose equality function could safely be replaced by
+ * datum_image_eq() in all cases can use this as their "equalimage" support
+ * function.
+ *
+ * Currently, we unconditionally assume that any B-Tree operator class that
+ * registers btequalimage as its support function 4 must be able to safely use
+ * optimizations like deduplication (i.e. we return true unconditionally). If
+ * it ever proved necessary to rescind support for an operator class, we could
+ * do that in a targeted fashion by doing something with the opcintype
+ * argument.
+ *-------------------------------------------------------------------------
+ */
+Datum
+btequalimage(PG_FUNCTION_ARGS)
+{
+ /* Oid opcintype = PG_GETARG_OID(0); */
+
+ PG_RETURN_BOOL(true);
+}
+
+/*-------------------------------------------------------------------------
* datumEstimateSpace
*
* Compute the amount of space that datumSerialize will require for a
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 1b351cbc688..875b02d6439 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -2783,6 +2783,26 @@ varstr_abbrev_abort(int memtupcount, SortSupport ssup)
return true;
}
+/*
+ * Generic equalimage support function for character type's operator classes.
+ * Disables the use of deduplication with nondeterministic collations.
+ */
+Datum
+btvarstrequalimage(PG_FUNCTION_ARGS)
+{
+ /* Oid opcintype = PG_GETARG_OID(0); */
+ Oid collid = PG_GET_COLLATION();
+
+ check_collation_set(collid);
+
+ if (lc_collate_is_c(collid) ||
+ collid == DEFAULT_COLLATION_OID ||
+ get_collation_isdeterministic(collid))
+ PG_RETURN_BOOL(true);
+ else
+ PG_RETURN_BOOL(false);
+}
+
Datum
text_larger(PG_FUNCTION_ARGS)
{
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 4a9764c2d22..1b90cbd9b58 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -522,7 +522,8 @@ my %tests = (
OPERATOR 4 >=(bigint,int4),
OPERATOR 5 >(bigint,int4),
FUNCTION 1 (int4, int4) btint4cmp(int4,int4),
- FUNCTION 2 (int4, int4) btint4sortsupport(internal);',
+ FUNCTION 2 (int4, int4) btint4sortsupport(internal),
+ FUNCTION 4 (int4, int4) btequalimage(oid);',
regexp => qr/^
\QALTER OPERATOR FAMILY dump_test.op_family USING btree ADD\E\n\s+
\QOPERATOR 1 <(bigint,integer) ,\E\n\s+
@@ -531,7 +532,8 @@ my %tests = (
\QOPERATOR 4 >=(bigint,integer) ,\E\n\s+
\QOPERATOR 5 >(bigint,integer) ,\E\n\s+
\QFUNCTION 1 (integer, integer) btint4cmp(integer,integer) ,\E\n\s+
- \QFUNCTION 2 (integer, integer) btint4sortsupport(internal);\E
+ \QFUNCTION 2 (integer, integer) btint4sortsupport(internal) ,\E\n\s+
+ \QFUNCTION 4 (integer, integer) btequalimage(oid);\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
@@ -1554,7 +1556,8 @@ my %tests = (
OPERATOR 4 >=(bigint,bigint),
OPERATOR 5 >(bigint,bigint),
FUNCTION 1 btint8cmp(bigint,bigint),
- FUNCTION 2 btint8sortsupport(internal);',
+ FUNCTION 2 btint8sortsupport(internal),
+ FUNCTION 4 btequalimage(oid);',
regexp => qr/^
\QCREATE OPERATOR CLASS dump_test.op_class\E\n\s+
\QFOR TYPE bigint USING btree FAMILY dump_test.op_family AS\E\n\s+
@@ -1564,7 +1567,8 @@ my %tests = (
\QOPERATOR 4 >=(bigint,bigint) ,\E\n\s+
\QOPERATOR 5 >(bigint,bigint) ,\E\n\s+
\QFUNCTION 1 (bigint, bigint) btint8cmp(bigint,bigint) ,\E\n\s+
- \QFUNCTION 2 (bigint, bigint) btint8sortsupport(internal);\E
+ \QFUNCTION 2 (bigint, bigint) btint8sortsupport(internal) ,\E\n\s+
+ \QFUNCTION 4 (bigint, bigint) btequalimage(oid);\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 20ace69dab4..e8d4d2b55b7 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -387,12 +387,17 @@ typedef struct BTMetaPageData
* an operator class may choose to offer a third amproc procedure
* (BTINRANGE_PROC), independently of whether it offers sortsupport.
* For full details, see doc/src/sgml/btree.sgml.
+ *
+ * To facilitate B-Tree deduplication, an operator class may choose to
+ * offer a forth amproc procedure (BTEQUALIMAGE_PROC). For full details,
+ * see doc/src/sgml/btree.sgml.
*/
#define BTORDER_PROC 1
#define BTSORTSUPPORT_PROC 2
#define BTINRANGE_PROC 3
-#define BTNProcs 3
+#define BTEQUALIMAGE_PROC 4
+#define BTNProcs 4
/*
* We need to be able to tell the difference between read and write
@@ -829,6 +834,7 @@ extern bool _bt_check_natts(Relation rel, bool heapkeyspace, Page page,
OffsetNumber offnum);
extern void _bt_check_third_page(Relation rel, Relation heap,
bool needheaptidspace, Page page, IndexTuple newtup);
+extern bool _bt_allequalimage(Relation rel, bool debugmessage);
/*
* prototypes for functions in nbtvalidate.c
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 2fe64b9d19a..1a5e5ce8d1d 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202002191
+#define CATALOG_VERSION_NO 202002261
#endif
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index c67768fcab2..75c0152b666 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -17,24 +17,37 @@
amprocrighttype => 'anyarray', amprocnum => '1', amproc => 'btarraycmp' },
{ amprocfamily => 'btree/bit_ops', amproclefttype => 'bit',
amprocrighttype => 'bit', amprocnum => '1', amproc => 'bitcmp' },
+{ amprocfamily => 'btree/bit_ops', amproclefttype => 'bit',
+ amprocrighttype => 'bit', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/bool_ops', amproclefttype => 'bool',
amprocrighttype => 'bool', amprocnum => '1', amproc => 'btboolcmp' },
+{ amprocfamily => 'btree/bool_ops', amproclefttype => 'bool',
+ amprocrighttype => 'bool', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/bpchar_ops', amproclefttype => 'bpchar',
amprocrighttype => 'bpchar', amprocnum => '1', amproc => 'bpcharcmp' },
{ amprocfamily => 'btree/bpchar_ops', amproclefttype => 'bpchar',
amprocrighttype => 'bpchar', amprocnum => '2',
amproc => 'bpchar_sortsupport' },
+{ amprocfamily => 'btree/bpchar_ops', amproclefttype => 'bpchar',
+ amprocrighttype => 'bpchar', amprocnum => '4',
+ amproc => 'btvarstrequalimage' },
{ amprocfamily => 'btree/bytea_ops', amproclefttype => 'bytea',
amprocrighttype => 'bytea', amprocnum => '1', amproc => 'byteacmp' },
{ amprocfamily => 'btree/bytea_ops', amproclefttype => 'bytea',
amprocrighttype => 'bytea', amprocnum => '2', amproc => 'bytea_sortsupport' },
+{ amprocfamily => 'btree/bytea_ops', amproclefttype => 'bytea',
+ amprocrighttype => 'bytea', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/char_ops', amproclefttype => 'char',
amprocrighttype => 'char', amprocnum => '1', amproc => 'btcharcmp' },
+{ amprocfamily => 'btree/char_ops', amproclefttype => 'char',
+ amprocrighttype => 'char', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/datetime_ops', amproclefttype => 'date',
amprocrighttype => 'date', amprocnum => '1', amproc => 'date_cmp' },
{ amprocfamily => 'btree/datetime_ops', amproclefttype => 'date',
amprocrighttype => 'date', amprocnum => '2', amproc => 'date_sortsupport' },
{ amprocfamily => 'btree/datetime_ops', amproclefttype => 'date',
+ amprocrighttype => 'date', amprocnum => '4', amproc => 'btequalimage' },
+{ amprocfamily => 'btree/datetime_ops', amproclefttype => 'date',
amprocrighttype => 'timestamp', amprocnum => '1',
amproc => 'date_cmp_timestamp' },
{ amprocfamily => 'btree/datetime_ops', amproclefttype => 'date',
@@ -46,6 +59,8 @@
amprocrighttype => 'timestamp', amprocnum => '2',
amproc => 'timestamp_sortsupport' },
{ amprocfamily => 'btree/datetime_ops', amproclefttype => 'timestamp',
+ amprocrighttype => 'timestamp', amprocnum => '4', amproc => 'btequalimage' },
+{ amprocfamily => 'btree/datetime_ops', amproclefttype => 'timestamp',
amprocrighttype => 'date', amprocnum => '1', amproc => 'timestamp_cmp_date' },
{ amprocfamily => 'btree/datetime_ops', amproclefttype => 'timestamp',
amprocrighttype => 'timestamptz', amprocnum => '1',
@@ -57,6 +72,9 @@
amprocrighttype => 'timestamptz', amprocnum => '2',
amproc => 'timestamp_sortsupport' },
{ amprocfamily => 'btree/datetime_ops', amproclefttype => 'timestamptz',
+ amprocrighttype => 'timestamptz', amprocnum => '4',
+ amproc => 'btequalimage' },
+{ amprocfamily => 'btree/datetime_ops', amproclefttype => 'timestamptz',
amprocrighttype => 'date', amprocnum => '1',
amproc => 'timestamptz_cmp_date' },
{ amprocfamily => 'btree/datetime_ops', amproclefttype => 'timestamptz',
@@ -96,11 +114,15 @@
{ amprocfamily => 'btree/network_ops', amproclefttype => 'inet',
amprocrighttype => 'inet', amprocnum => '2',
amproc => 'network_sortsupport' },
+{ amprocfamily => 'btree/network_ops', amproclefttype => 'inet',
+ amprocrighttype => 'inet', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/integer_ops', amproclefttype => 'int2',
amprocrighttype => 'int2', amprocnum => '1', amproc => 'btint2cmp' },
{ amprocfamily => 'btree/integer_ops', amproclefttype => 'int2',
amprocrighttype => 'int2', amprocnum => '2', amproc => 'btint2sortsupport' },
{ amprocfamily => 'btree/integer_ops', amproclefttype => 'int2',
+ amprocrighttype => 'int2', amprocnum => '4', amproc => 'btequalimage' },
+{ amprocfamily => 'btree/integer_ops', amproclefttype => 'int2',
amprocrighttype => 'int4', amprocnum => '1', amproc => 'btint24cmp' },
{ amprocfamily => 'btree/integer_ops', amproclefttype => 'int2',
amprocrighttype => 'int8', amprocnum => '1', amproc => 'btint28cmp' },
@@ -118,6 +140,8 @@
{ amprocfamily => 'btree/integer_ops', amproclefttype => 'int4',
amprocrighttype => 'int4', amprocnum => '2', amproc => 'btint4sortsupport' },
{ amprocfamily => 'btree/integer_ops', amproclefttype => 'int4',
+ amprocrighttype => 'int4', amprocnum => '4', amproc => 'btequalimage' },
+{ amprocfamily => 'btree/integer_ops', amproclefttype => 'int4',
amprocrighttype => 'int8', amprocnum => '1', amproc => 'btint48cmp' },
{ amprocfamily => 'btree/integer_ops', amproclefttype => 'int4',
amprocrighttype => 'int2', amprocnum => '1', amproc => 'btint42cmp' },
@@ -135,6 +159,8 @@
{ amprocfamily => 'btree/integer_ops', amproclefttype => 'int8',
amprocrighttype => 'int8', amprocnum => '2', amproc => 'btint8sortsupport' },
{ amprocfamily => 'btree/integer_ops', amproclefttype => 'int8',
+ amprocrighttype => 'int8', amprocnum => '4', amproc => 'btequalimage' },
+{ amprocfamily => 'btree/integer_ops', amproclefttype => 'int8',
amprocrighttype => 'int4', amprocnum => '1', amproc => 'btint84cmp' },
{ amprocfamily => 'btree/integer_ops', amproclefttype => 'int8',
amprocrighttype => 'int2', amprocnum => '1', amproc => 'btint82cmp' },
@@ -146,11 +172,15 @@
{ amprocfamily => 'btree/interval_ops', amproclefttype => 'interval',
amprocrighttype => 'interval', amprocnum => '3',
amproc => 'in_range(interval,interval,interval,bool,bool)' },
+{ amprocfamily => 'btree/interval_ops', amproclefttype => 'interval',
+ amprocrighttype => 'interval', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/macaddr_ops', amproclefttype => 'macaddr',
amprocrighttype => 'macaddr', amprocnum => '1', amproc => 'macaddr_cmp' },
{ amprocfamily => 'btree/macaddr_ops', amproclefttype => 'macaddr',
amprocrighttype => 'macaddr', amprocnum => '2',
amproc => 'macaddr_sortsupport' },
+{ amprocfamily => 'btree/macaddr_ops', amproclefttype => 'macaddr',
+ amprocrighttype => 'macaddr', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/numeric_ops', amproclefttype => 'numeric',
amprocrighttype => 'numeric', amprocnum => '1', amproc => 'numeric_cmp' },
{ amprocfamily => 'btree/numeric_ops', amproclefttype => 'numeric',
@@ -163,62 +193,92 @@
amprocrighttype => 'oid', amprocnum => '1', amproc => 'btoidcmp' },
{ amprocfamily => 'btree/oid_ops', amproclefttype => 'oid',
amprocrighttype => 'oid', amprocnum => '2', amproc => 'btoidsortsupport' },
+{ amprocfamily => 'btree/oid_ops', amproclefttype => 'oid',
+ amprocrighttype => 'oid', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/oidvector_ops', amproclefttype => 'oidvector',
amprocrighttype => 'oidvector', amprocnum => '1',
amproc => 'btoidvectorcmp' },
+{ amprocfamily => 'btree/oidvector_ops', amproclefttype => 'oidvector',
+ amprocrighttype => 'oidvector', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/text_ops', amproclefttype => 'text',
amprocrighttype => 'text', amprocnum => '1', amproc => 'bttextcmp' },
{ amprocfamily => 'btree/text_ops', amproclefttype => 'text',
amprocrighttype => 'text', amprocnum => '2', amproc => 'bttextsortsupport' },
+{ amprocfamily => 'btree/text_ops', amproclefttype => 'text',
+ amprocrighttype => 'text', amprocnum => '4', amproc => 'btvarstrequalimage' },
{ amprocfamily => 'btree/text_ops', amproclefttype => 'name',
amprocrighttype => 'name', amprocnum => '1', amproc => 'btnamecmp' },
{ amprocfamily => 'btree/text_ops', amproclefttype => 'name',
amprocrighttype => 'name', amprocnum => '2', amproc => 'btnamesortsupport' },
{ amprocfamily => 'btree/text_ops', amproclefttype => 'name',
+ amprocrighttype => 'name', amprocnum => '4', amproc => 'btvarstrequalimage' },
+{ amprocfamily => 'btree/text_ops', amproclefttype => 'name',
amprocrighttype => 'text', amprocnum => '1', amproc => 'btnametextcmp' },
{ amprocfamily => 'btree/text_ops', amproclefttype => 'text',
amprocrighttype => 'name', amprocnum => '1', amproc => 'bttextnamecmp' },
{ amprocfamily => 'btree/time_ops', amproclefttype => 'time',
amprocrighttype => 'time', amprocnum => '1', amproc => 'time_cmp' },
{ amprocfamily => 'btree/time_ops', amproclefttype => 'time',
+ amprocrighttype => 'time', amprocnum => '4', amproc => 'btequalimage' },
+{ amprocfamily => 'btree/time_ops', amproclefttype => 'time',
amprocrighttype => 'interval', amprocnum => '3',
amproc => 'in_range(time,time,interval,bool,bool)' },
{ amprocfamily => 'btree/timetz_ops', amproclefttype => 'timetz',
amprocrighttype => 'timetz', amprocnum => '1', amproc => 'timetz_cmp' },
{ amprocfamily => 'btree/timetz_ops', amproclefttype => 'timetz',
+ amprocrighttype => 'timetz', amprocnum => '4', amproc => 'btequalimage' },
+{ amprocfamily => 'btree/timetz_ops', amproclefttype => 'timetz',
amprocrighttype => 'interval', amprocnum => '3',
amproc => 'in_range(timetz,timetz,interval,bool,bool)' },
{ amprocfamily => 'btree/varbit_ops', amproclefttype => 'varbit',
amprocrighttype => 'varbit', amprocnum => '1', amproc => 'varbitcmp' },
+{ amprocfamily => 'btree/varbit_ops', amproclefttype => 'varbit',
+ amprocrighttype => 'varbit', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/text_pattern_ops', amproclefttype => 'text',
amprocrighttype => 'text', amprocnum => '1', amproc => 'bttext_pattern_cmp' },
{ amprocfamily => 'btree/text_pattern_ops', amproclefttype => 'text',
amprocrighttype => 'text', amprocnum => '2',
amproc => 'bttext_pattern_sortsupport' },
+{ amprocfamily => 'btree/text_pattern_ops', amproclefttype => 'text',
+ amprocrighttype => 'text', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/bpchar_pattern_ops', amproclefttype => 'bpchar',
amprocrighttype => 'bpchar', amprocnum => '1',
amproc => 'btbpchar_pattern_cmp' },
{ amprocfamily => 'btree/bpchar_pattern_ops', amproclefttype => 'bpchar',
amprocrighttype => 'bpchar', amprocnum => '2',
amproc => 'btbpchar_pattern_sortsupport' },
+{ amprocfamily => 'btree/bpchar_pattern_ops', amproclefttype => 'bpchar',
+ amprocrighttype => 'bpchar', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/money_ops', amproclefttype => 'money',
amprocrighttype => 'money', amprocnum => '1', amproc => 'cash_cmp' },
+{ amprocfamily => 'btree/money_ops', amproclefttype => 'money',
+ amprocrighttype => 'money', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/tid_ops', amproclefttype => 'tid',
amprocrighttype => 'tid', amprocnum => '1', amproc => 'bttidcmp' },
+{ amprocfamily => 'btree/tid_ops', amproclefttype => 'tid',
+ amprocrighttype => 'tid', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/uuid_ops', amproclefttype => 'uuid',
amprocrighttype => 'uuid', amprocnum => '1', amproc => 'uuid_cmp' },
{ amprocfamily => 'btree/uuid_ops', amproclefttype => 'uuid',
amprocrighttype => 'uuid', amprocnum => '2', amproc => 'uuid_sortsupport' },
+{ amprocfamily => 'btree/uuid_ops', amproclefttype => 'uuid',
+ amprocrighttype => 'uuid', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/record_ops', amproclefttype => 'record',
amprocrighttype => 'record', amprocnum => '1', amproc => 'btrecordcmp' },
{ amprocfamily => 'btree/record_image_ops', amproclefttype => 'record',
amprocrighttype => 'record', amprocnum => '1', amproc => 'btrecordimagecmp' },
{ amprocfamily => 'btree/pg_lsn_ops', amproclefttype => 'pg_lsn',
amprocrighttype => 'pg_lsn', amprocnum => '1', amproc => 'pg_lsn_cmp' },
+{ amprocfamily => 'btree/pg_lsn_ops', amproclefttype => 'pg_lsn',
+ amprocrighttype => 'pg_lsn', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/macaddr8_ops', amproclefttype => 'macaddr8',
amprocrighttype => 'macaddr8', amprocnum => '1', amproc => 'macaddr8_cmp' },
+{ amprocfamily => 'btree/macaddr8_ops', amproclefttype => 'macaddr8',
+ amprocrighttype => 'macaddr8', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/enum_ops', amproclefttype => 'anyenum',
amprocrighttype => 'anyenum', amprocnum => '1', amproc => 'enum_cmp' },
+{ amprocfamily => 'btree/enum_ops', amproclefttype => 'anyenum',
+ amprocrighttype => 'anyenum', amprocnum => '4', amproc => 'btequalimage' },
{ amprocfamily => 'btree/tsvector_ops', amproclefttype => 'tsvector',
amprocrighttype => 'tsvector', amprocnum => '1', amproc => 'tsvector_cmp' },
{ amprocfamily => 'btree/tsquery_ops', amproclefttype => 'tsquery',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index eb3c1a88d14..07a86c7b7b3 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -1013,6 +1013,9 @@
{ oid => '3255', descr => 'sort support',
proname => 'bttextsortsupport', prorettype => 'void',
proargtypes => 'internal', prosrc => 'bttextsortsupport' },
+{ oid => '8505', descr => 'equal image',
+ proname => 'btvarstrequalimage', prorettype => 'bool', proargtypes => 'oid',
+ prosrc => 'btvarstrequalimage' },
{ oid => '377', descr => 'less-equal-greater',
proname => 'cash_cmp', proleakproof => 't', prorettype => 'int4',
proargtypes => 'money money', prosrc => 'cash_cmp' },
@@ -9483,6 +9486,9 @@
{ oid => '3187', descr => 'less-equal-greater based on byte images',
proname => 'btrecordimagecmp', prorettype => 'int4',
proargtypes => 'record record', prosrc => 'btrecordimagecmp' },
+{ oid => '8506', descr => 'equal image',
+ proname => 'btequalimage', prorettype => 'bool', proargtypes => 'oid',
+ prosrc => 'btequalimage' },
# Extensions
{ oid => '3082', descr => 'list available extensions',
diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out
index ac5183c90e6..ba5ce7a17e5 100644
--- a/src/test/regress/expected/alter_generic.out
+++ b/src/test/regress/expected/alter_generic.out
@@ -354,9 +354,9 @@ ERROR: invalid operator number 0, must be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ERROR: operator argument types must be specified in ALTER OPERATOR FAMILY
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
-ERROR: invalid function number 0, must be between 1 and 3
+ERROR: invalid function number 0, must be between 1 and 4
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
-ERROR: invalid function number 6, must be between 1 and 3
+ERROR: invalid function number 6, must be between 1 and 4
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD STORAGE invalid_storage; -- Ensure STORAGE is not a part of ALTER OPERATOR FAMILY
ERROR: STORAGE cannot be specified in ALTER OPERATOR FAMILY
DROP OPERATOR FAMILY alt_opf4 USING btree;
@@ -493,6 +493,10 @@ ALTER OPERATOR FAMILY alt_opf18 USING btree ADD
OPERATOR 4 >= (int4, int2) ,
OPERATOR 5 > (int4, int2) ,
FUNCTION 1 btint42cmp(int4, int2);
+-- Should fail. Not allowed to have cross-type equalimage function.
+ALTER OPERATOR FAMILY alt_opf18 USING btree
+ ADD FUNCTION 4 (int4, int2) btequalimage(oid);
+ERROR: btree equal image functions must not be cross-type
ALTER OPERATOR FAMILY alt_opf18 USING btree DROP FUNCTION 2 (int4, int4);
ERROR: function 2(integer,integer) does not exist in operator family "alt_opf18"
DROP OPERATOR FAMILY alt_opf18 USING btree;
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index c19740e5db8..fb6c029e3d2 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -2111,6 +2111,42 @@ WHERE p1.amproc = p2.oid AND
--------------+--------+--------
(0 rows)
+-- Almost all of the core distribution's Btree opclasses can use one of the
+-- two generic "equalimage" functions as their support function 4. Look for
+-- opclasses that don't allow deduplication unconditionally here.
+--
+-- Newly added Btree opclasses don't have to support deduplication. It will
+-- usually be trivial to add support, though. Note that the expected output
+-- of this part of the test will need to be updated when a new opclass cannot
+-- support deduplication (by using btequalimage).
+SELECT amp.amproc::regproc AS proc, opf.opfname AS opfamily_name,
+ opc.opcname AS opclass_name, opc.opcintype::regtype AS opcintype
+FROM pg_am AS am
+JOIN pg_opclass AS opc ON opc.opcmethod = am.oid
+JOIN pg_opfamily AS opf ON opc.opcfamily = opf.oid
+LEFT JOIN pg_amproc AS amp ON amp.amprocfamily = opf.oid AND
+ amp.amproclefttype = opc.opcintype AND amp.amprocnum = 4
+WHERE am.amname = 'btree' AND
+ amp.amproc IS DISTINCT FROM 'btequalimage'::regproc
+ORDER BY 1, 2, 3;
+ proc | opfamily_name | opclass_name | opcintype
+--------------------+------------------+------------------+------------------
+ btvarstrequalimage | bpchar_ops | bpchar_ops | character
+ btvarstrequalimage | text_ops | name_ops | name
+ btvarstrequalimage | text_ops | text_ops | text
+ btvarstrequalimage | text_ops | varchar_ops | text
+ | array_ops | array_ops | anyarray
+ | float_ops | float4_ops | real
+ | float_ops | float8_ops | double precision
+ | jsonb_ops | jsonb_ops | jsonb
+ | numeric_ops | numeric_ops | numeric
+ | range_ops | range_ops | anyrange
+ | record_image_ops | record_image_ops | record
+ | record_ops | record_ops | record
+ | tsquery_ops | tsquery_ops | tsquery
+ | tsvector_ops | tsvector_ops | tsvector
+(14 rows)
+
-- **************** pg_index ****************
-- Look for illegal values in pg_index fields.
SELECT p1.indexrelid, p1.indrelid
diff --git a/src/test/regress/sql/alter_generic.sql b/src/test/regress/sql/alter_generic.sql
index 9eeea2a87e4..223d66bc2d5 100644
--- a/src/test/regress/sql/alter_generic.sql
+++ b/src/test/regress/sql/alter_generic.sql
@@ -430,6 +430,9 @@ ALTER OPERATOR FAMILY alt_opf18 USING btree ADD
OPERATOR 4 >= (int4, int2) ,
OPERATOR 5 > (int4, int2) ,
FUNCTION 1 btint42cmp(int4, int2);
+-- Should fail. Not allowed to have cross-type equalimage function.
+ALTER OPERATOR FAMILY alt_opf18 USING btree
+ ADD FUNCTION 4 (int4, int2) btequalimage(oid);
ALTER OPERATOR FAMILY alt_opf18 USING btree DROP FUNCTION 2 (int4, int4);
DROP OPERATOR FAMILY alt_opf18 USING btree;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 624bea46cea..8351b6469ab 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1323,6 +1323,24 @@ WHERE p1.amproc = p2.oid AND
p1.amproclefttype != p1.amprocrighttype AND
p2.provolatile = 'v';
+-- Almost all of the core distribution's Btree opclasses can use one of the
+-- two generic "equalimage" functions as their support function 4. Look for
+-- opclasses that don't allow deduplication unconditionally here.
+--
+-- Newly added Btree opclasses don't have to support deduplication. It will
+-- usually be trivial to add support, though. Note that the expected output
+-- of this part of the test will need to be updated when a new opclass cannot
+-- support deduplication (by using btequalimage).
+SELECT amp.amproc::regproc AS proc, opf.opfname AS opfamily_name,
+ opc.opcname AS opclass_name, opc.opcintype::regtype AS opcintype
+FROM pg_am AS am
+JOIN pg_opclass AS opc ON opc.opcmethod = am.oid
+JOIN pg_opfamily AS opf ON opc.opcfamily = opf.oid
+LEFT JOIN pg_amproc AS amp ON amp.amprocfamily = opf.oid AND
+ amp.amproclefttype = opc.opcintype AND amp.amprocnum = 4
+WHERE am.amname = 'btree' AND
+ amp.amproc IS DISTINCT FROM 'btequalimage'::regproc
+ORDER BY 1, 2, 3;
-- **************** pg_index ****************