1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-30 11:03:19 +03:00

Restructure operator classes to allow improved handling of cross-data-type

cases.  Operator classes now exist within "operator families".  While most
families are equivalent to a single class, related classes can be grouped
into one family to represent the fact that they are semantically compatible.
Cross-type operators are now naturally adjunct parts of a family, without
having to wedge them into a particular opclass as we had done originally.

This commit restructures the catalogs and cleans up enough of the fallout so
that everything still works at least as well as before, but most of the work
needed to actually improve the planner's behavior will come later.  Also,
there are not yet CREATE/DROP/ALTER OPERATOR FAMILY commands; the only way
to create a new family right now is to allow CREATE OPERATOR CLASS to make
one by default.  I owe some more documentation work, too.  But that can all
be done in smaller pieces once this infrastructure is in place.
This commit is contained in:
Tom Lane
2006-12-23 00:43:13 +00:00
parent d31ccb6c3e
commit a78fcfb512
76 changed files with 4753 additions and 4100 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.138 2006/12/18 18:56:28 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.139 2006/12/23 00:43:08 tgl Exp $ -->
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
-->
@ -160,7 +160,7 @@
<row>
<entry><link linkend="catalog-pg-opclass"><structname>pg_opclass</structname></link></entry>
<entry>index access method operator classes</entry>
<entry>access method operator classes</entry>
</row>
<row>
@ -168,6 +168,11 @@
<entry>operators</entry>
</row>
<row>
<entry><link linkend="catalog-pg-opfamily"><structname>pg_opfamily</structname></link></entry>
<entry>access method operator families</entry>
</row>
<row>
<entry><link linkend="catalog-pg-pltemplate"><structname>pg_pltemplate</structname></link></entry>
<entry>template data for procedural languages</entry>
@ -516,9 +521,11 @@
</indexterm>
<para>
The catalog <structname>pg_amop</structname> stores information about operators
associated with index access method operator classes. There is one
row for each operator that is a member of an operator class.
The catalog <structname>pg_amop</structname> stores information about
operators associated with access method operator families. There is one
row for each operator that is a member of an operator family. An operator
can appear in more than one family, but may not appear in more than one
position within a family.
</para>
<table>
@ -536,18 +543,24 @@
<tbody>
<row>
<entry><structfield>amopclaid</structfield></entry>
<entry><structfield>amopfamily</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-opclass"><structname>pg_opclass</structname></link>.oid</literal></entry>
<entry>The index operator class this entry is for</entry>
<entry><literal><link linkend="catalog-pg-opfamily"><structname>pg_opfamily</structname></link>.oid</literal></entry>
<entry>The operator family this entry is for</entry>
</row>
<row>
<entry><structfield>amopsubtype</structfield></entry>
<entry><structfield>amoplefttype</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
<entry>Subtype to distinguish multiple entries for one strategy;
zero for default</entry>
<entry>Left-hand input data type of operator</entry>
</row>
<row>
<entry><structfield>amoprighttype</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
<entry>Right-hand input data type of operator</entry>
</row>
<row>
@ -571,10 +584,27 @@
<entry>OID of the operator</entry>
</row>
<row>
<entry><structfield>amopmethod</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-am"><structname>pg_am</structname></link>.oid</literal></entry>
<entry>Index access method operator family is for</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
An entry's <structfield>amopmethod</> must match the
<structname>opfmethod</> of its containing operator family (including
<structfield>amopmethod</> here is an intentional denormalization of the
catalog structure for performance reasons). Also,
<structfield>amoplefttype</> and <structfield>amoprighttype</> must match
the <structfield>oprleft</> and <structfield>oprright</> fields of the
referenced <structname>pg_operator</> entry.
</para>
</sect1>
@ -586,10 +616,9 @@
</indexterm>
<para>
The catalog <structname>pg_amproc</structname> stores information about support
procedures
associated with index access method operator classes. There is one
row for each support procedure belonging to an operator class.
The catalog <structname>pg_amproc</structname> stores information about
support procedures associated with access method operator families. There
is one row for each support procedure belonging to an operator family.
</para>
<table>
@ -607,17 +636,24 @@
<tbody>
<row>
<entry><structfield>amopclaid</structfield></entry>
<entry><structfield>amprocfamily</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-opclass"><structname>pg_opclass</structname></link>.oid</literal></entry>
<entry>The index operator class this entry is for</entry>
<entry><literal><link linkend="catalog-pg-opfamily"><structname>pg_opfamily</structname></link>.oid</literal></entry>
<entry>The operator family this entry is for</entry>
</row>
<row>
<entry><structfield>amprocsubtype</structfield></entry>
<entry><structfield>amproclefttype</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
<entry>Subtype, if cross-type routine, else zero</entry>
<entry>Left-hand input data type of associated operator</entry>
</row>
<row>
<entry><structfield>amprocrighttype</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
<entry>Right-hand input data type of associated operator</entry>
</row>
<row>
@ -638,6 +674,18 @@
</tgroup>
</table>
<para>
The usual interpretation of the
<structfield>amproclefttype</> and <structfield>amprocrighttype</> fields
is that they identify the left and right input types of the operator(s)
that a particular support procedure supports. For some access methods
these match the input data type(s) of the support procedure itself, for
others not. There is a notion of <quote>default</> support procedures for
an index, which are those with <structfield>amproclefttype</> and
<structfield>amprocrighttype</> both equal to the index opclass's
<structfield>opcintype</>.
</para>
</sect1>
@ -2843,9 +2891,11 @@
The catalog <structname>pg_opclass</structname> defines
index access method operator classes. Each operator class defines
semantics for index columns of a particular data type and a particular
index access method. Note that there can be multiple operator classes
for a given data type/access method combination, thus supporting multiple
behaviors.
index access method. An operator class essentially specifies that a
particular operator family is applicable to a particular indexable column
data type. The set of operators from the family that are actually usable
with the indexed column are whichever ones accept the column's data type
as their lefthand input.
</para>
<para>
@ -2867,7 +2917,7 @@
<tbody>
<row>
<entry><structfield>opcamid</structfield></entry>
<entry><structfield>opcmethod</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-am"><structname>pg_am</structname></link>.oid</literal></entry>
<entry>Index access method operator class is for</entry>
@ -2894,6 +2944,13 @@
<entry>Owner of the operator class</entry>
</row>
<row>
<entry><structfield>opcfamily</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-opfamily"><structname>pg_opfamily</structname></link>.oid</literal></entry>
<entry>Operator family containing the operator class</entry>
</row>
<row>
<entry><structfield>opcintype</structfield></entry>
<entry><type>oid</type></entry>
@ -2920,14 +2977,11 @@
</table>
<para>
The majority of the information defining an operator class is actually
not in its <structname>pg_opclass</structname> row, but in the associated
rows in <structname>pg_amop</structname> and
<structname>pg_amproc</structname>. Those rows are considered to be
part of the operator class definition &mdash; this is not unlike the way
that a relation is defined by a single <structname>pg_class</structname>
row plus associated rows in <structname>pg_attribute</structname> and
other tables.
An operator class's <structfield>opcmethod</> must match the
<structname>opfmethod</> of its containing operator family.
Also, there must be no more than one <structname>pg_opclass</structname>
row having <structname>opcdefault</> true for any given combination of
<structname>opcmethod</> and <structname>opcintype</>.
</para>
</sect1>
@ -2993,6 +3047,13 @@
</entry>
</row>
<row>
<entry><structfield>oprcanmerge</structfield></entry>
<entry><type>bool</type></entry>
<entry></entry>
<entry>This operator supports merge joins</entry>
</row>
<row>
<entry><structfield>oprcanhash</structfield></entry>
<entry><type>bool</type></entry>
@ -3035,46 +3096,6 @@
<entry>Negator of this operator, if any</entry>
</row>
<row>
<entry><structfield>oprlsortop</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</literal></entry>
<entry>
If this operator supports merge joins, the operator that sorts
the type of the left-hand operand (<literal>L&lt;L</>)
</entry>
</row>
<row>
<entry><structfield>oprrsortop</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</literal></entry>
<entry>
If this operator supports merge joins, the operator that sorts
the type of the right-hand operand (<literal>R&lt;R</>)
</entry>
</row>
<row>
<entry><structfield>oprltcmpop</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</literal></entry>
<entry>
If this operator supports merge joins, the less-than operator that
compares the left and right operand types (<literal>L&lt;R</>)
</entry>
</row>
<row>
<entry><structfield>oprgtcmpop</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</literal></entry>
<entry>
If this operator supports merge joins, the greater-than operator that
compares the left and right operand types (<literal>L&gt;R</>)
</entry>
</row>
<row>
<entry><structfield>oprcode</structfield></entry>
<entry><type>regproc</type></entry>
@ -3107,6 +3128,86 @@
</sect1>
<sect1 id="catalog-pg-opfamily">
<title><structname>pg_opfamily</structname></title>
<indexterm zone="catalog-pg-opfamily">
<primary>pg_opfamily</primary>
</indexterm>
<para>
The catalog <structname>pg_opfamily</structname> defines operator families.
Each operator family is a collection of operators and associated
support routines that implement the semantics specified for a particular
index access method. Furthermore, the operators in a family are all
<quote>compatible</>, in a way that depends on the access method.
The operator family concept allows cross-data-type operators to be used
with indexes and to be reasoned about using knowledge of access method
semantics.
</para>
<para>
Operator families are described at length in <xref linkend="xindex">.
</para>
<table>
<title><structname>pg_opfamily</> Columns</title>
<tgroup cols=4>
<thead>
<row>
<entry>Name</entry>
<entry>Type</entry>
<entry>References</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><structfield>opfmethod</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-am"><structname>pg_am</structname></link>.oid</literal></entry>
<entry>Index access method operator family is for</entry>
</row>
<row>
<entry><structfield>opfname</structfield></entry>
<entry><type>name</type></entry>
<entry></entry>
<entry>Name of this operator family</entry>
</row>
<row>
<entry><structfield>opfnamespace</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
<entry>Namespace of this operator family</entry>
</row>
<row>
<entry><structfield>opfowner</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
<entry>Owner of the operator family</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
The majority of the information defining an operator family is not in its
<structname>pg_opfamily</structname> row, but in the associated rows in
<link linkend="catalog-pg-amop"><structname>pg_amop</structname></link>,
<link linkend="catalog-pg-amproc"><structname>pg_amproc</structname></link>,
and
<link linkend="catalog-pg-opclass"><structname>pg_opclass</structname></link>.
</para>
</sect1>
<sect1 id="catalog-pg-pltemplate">
<title><structname>pg_pltemplate</structname></title>

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.18 2006/09/16 00:30:14 momjian Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.19 2006/12/23 00:43:08 tgl Exp $ -->
<chapter id="indexam">
<title>Index Access Method Interface Definition</title>
@ -63,13 +63,15 @@
<para>
To be useful, an index access method must also have one or more
<firstterm>operator families</> and
<firstterm>operator classes</> defined in
<link linkend="catalog-pg-opfamily"><structname>pg_opfamily</structname></link>,
<link linkend="catalog-pg-opclass"><structname>pg_opclass</structname></link>,
<link linkend="catalog-pg-amop"><structname>pg_amop</structname></link>, and
<link linkend="catalog-pg-amproc"><structname>pg_amproc</structname></link>.
These entries allow the planner
to determine what kinds of query qualifications can be used with
indexes of this access method. Operator classes are described
indexes of this access method. Operator families and classes are described
in <xref linkend="xindex">, which is prerequisite material for reading
this chapter.
</para>
@ -409,14 +411,14 @@ amrestrpos (IndexScanDesc scan);
A scan key is the internal representation of a <literal>WHERE</> clause of
the form <replaceable>index_key</> <replaceable>operator</>
<replaceable>constant</>, where the index key is one of the columns of the
index and the operator is one of the members of the operator class
index and the operator is one of the members of the operator family
associated with that index column. An index scan has zero or more scan
keys, which are implicitly ANDed &mdash; the returned tuples are expected
to satisfy all the indicated conditions.
</para>
<para>
The operator class may indicate that the index is <firstterm>lossy</> for a
The operator family may indicate that the index is <firstterm>lossy</> for a
particular operator; this implies that the index scan will return all the
entries that pass the scan key, plus possibly additional entries that do
not. The core system's index-scan machinery will then apply that operator
@ -429,7 +431,7 @@ amrestrpos (IndexScanDesc scan);
Note that it is entirely up to the access method to ensure that it
correctly finds all and only the entries passing all the given scan keys.
Also, the core system will simply hand off all the <literal>WHERE</>
clauses that match the index keys and operator classes, without any
clauses that match the index keys and operator families, without any
semantic analysis to determine whether they are redundant or
contradictory. As an example, given
<literal>WHERE x &gt; 4 AND x &gt; 14</> where <literal>x</> is a b-tree

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/indices.sgml,v 1.66 2006/12/01 23:46:46 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/indices.sgml,v 1.67 2006/12/23 00:43:08 tgl Exp $ -->
<chapter id="indexes">
<title id="indexes-title">Indexes</title>
@ -784,12 +784,16 @@ CREATE UNIQUE INDEX tests_success_constraint ON tests (subject, target)
<sect1 id="indexes-opclass">
<title>Operator Classes</title>
<title>Operator Classes and Operator Families</title>
<indexterm zone="indexes-opclass">
<primary>operator class</primary>
</indexterm>
<indexterm zone="indexes-opclass">
<primary>operator family</primary>
</indexterm>
<para>
An index definition may specify an <firstterm>operator
class</firstterm> for each column of an index.
@ -854,20 +858,32 @@ CREATE INDEX test_index ON test_table (col varchar_pattern_ops);
SELECT am.amname AS index_method,
opc.opcname AS opclass_name
FROM pg_am am, pg_opclass opc
WHERE opc.opcamid = am.oid
WHERE opc.opcmethod = am.oid
ORDER BY index_method, opclass_name;
</programlisting>
</para>
It can be extended to show all the operators included in each class:
<para>
An operator class is actually just a subset of a larger structure called an
<firstterm>operator family</>. In cases where several data types have
similar behaviors, it is frequently useful to define cross-data-type
operators and allow these to work with indexes. To do this, the operator
classes for each of the types must be grouped into the same operator
family. The cross-type operators are members of the family, but are not
associated with any single class within the family.
</para>
<para>
This query shows all defined operator families and all
the operators included in each family:
<programlisting>
SELECT am.amname AS index_method,
opc.opcname AS opclass_name,
opr.oid::regoperator AS opclass_operator
FROM pg_am am, pg_opclass opc, pg_amop amop, pg_operator opr
WHERE opc.opcamid = am.oid AND
amop.amopclaid = opc.oid AND
amop.amopopr = opr.oid
ORDER BY index_method, opclass_name, opclass_operator;
opf.opfname AS opfamily_name,
amop.amopopr::regoperator AS opfamily_operator
FROM pg_am am, pg_opfamily opf, pg_amop amop
WHERE opf.opfmethod = am.oid AND
amop.amopfamily = opf.oid
ORDER BY index_method, opfamily_name, opfamily_operator;
</programlisting>
</para>
</sect1>

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_operator.sgml,v 1.45 2006/09/16 00:30:17 momjian Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/create_operator.sgml,v 1.46 2006/12/23 00:43:08 tgl Exp $
PostgreSQL documentation
-->
@ -26,8 +26,6 @@ CREATE OPERATOR <replaceable>name</replaceable> (
[, COMMUTATOR = <replaceable class="parameter">com_op</replaceable> ] [, NEGATOR = <replaceable class="parameter">neg_op</replaceable> ]
[, RESTRICT = <replaceable class="parameter">res_proc</replaceable> ] [, JOIN = <replaceable class="parameter">join_proc</replaceable> ]
[, HASHES ] [, MERGES ]
[, SORT1 = <replaceable class="parameter">left_sort_op</replaceable> ] [, SORT2 = <replaceable class="parameter">right_sort_op</replaceable> ]
[, LTCMP = <replaceable class="parameter">less_than_op</replaceable> ] [, GTCMP = <replaceable class="parameter">greater_than_op</replaceable> ]
)
</synopsis>
</refsynopsisdiv>
@ -202,46 +200,6 @@ CREATE OPERATOR <replaceable>name</replaceable> (
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">left_sort_op</replaceable></term>
<listitem>
<para>
If this operator can support a merge join, the less-than
operator that sorts the left-hand data type of this operator.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">right_sort_op</replaceable></term>
<listitem>
<para>
If this operator can support a merge join, the less-than
operator that sorts the right-hand data type of this operator.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">less_than_op</replaceable></term>
<listitem>
<para>
If this operator can support a merge join, the less-than
operator that compares the input data types of this operator.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">greater_than_op</replaceable></term>
<listitem>
<para>
If this operator can support a merge join, the greater-than
operator that compares the input data types of this operator.
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
@ -261,6 +219,16 @@ COMMUTATOR = OPERATOR(myschema.===) ,
Refer to <xref linkend="xoper"> for further information.
</para>
<para>
The obsolete options <literal>SORT1</>, <literal>SORT2</>,
<literal>LTCMP</>, and <literal>GTCMP</> were formerly used to
specify the names of sort operators associated with a mergejoinable
operator. This is no longer necessary, since information about
associated operators is found by looking at btree operator families
instead. If one of these options is given, it is ignored except
for implicitly setting <literal>MERGES</> true.
</para>
<para>
Use <xref linkend="sql-dropoperator"
endterm="sql-dropoperator-title"> to delete user-defined operators
@ -285,11 +253,7 @@ CREATE OPERATOR === (
NEGATOR = !==,
RESTRICT = area_restriction_procedure,
JOIN = area_join_procedure,
HASHES,
SORT1 = &lt;&lt;&lt;,
SORT2 = &lt;&lt;&lt;
-- Since sort operators were given, MERGES is implied.
-- LTCMP and GTCMP are assumed to be &lt; and &gt; respectively
HASHES, MERGES
);
</programlisting>
</para>

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/xoper.sgml,v 1.36 2006/09/16 00:30:16 momjian Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/xoper.sgml,v 1.37 2006/12/23 00:43:08 tgl Exp $ -->
<sect1 id="xoper">
<title>User-Defined Operators</title>
@ -342,13 +342,13 @@ table1.column1 OP table2.column2
<para>
To be marked <literal>HASHES</literal>, the join operator must appear
in a hash index operator class. This is not enforced when you create
the operator, since of course the referencing operator class couldn't
in a hash index operator family. This is not enforced when you create
the operator, since of course the referencing operator family couldn't
exist yet. But attempts to use the operator in hash joins will fail
at run time if no such operator class exists. The system needs the
operator class to find the data-type-specific hash function for the
at run time if no such operator family exists. The system needs the
operator family to find the data-type-specific hash function for the
operator's input data type. Of course, you must also supply a suitable
hash function before you can create the operator class.
hash function before you can create the operator family.
</para>
<para>
@ -390,7 +390,7 @@ table1.column1 OP table2.column2
</sect2>
<sect2>
<title><literal>MERGES</> (<literal>SORT1</>, <literal>SORT2</>, <literal>LTCMP</>, <literal>GTCMP</>)</title>
<title><literal>MERGES</></title>
<para>
The <literal>MERGES</literal> clause, if present, tells the system that
@ -418,36 +418,13 @@ table1.column1 OP table2.column2
</para>
<para>
Execution of a merge join requires that the system be able to identify
four operators related to the merge-join equality operator: less-than
comparison for the left operand data type, less-than comparison for the
right operand data type, less-than comparison between the two data types, and
greater-than comparison between the two data types. (These are actually
four distinct operators if the merge-joinable operator has two different
operand data types; but when the operand types are the same the three
less-than operators are all the same operator.)
It is possible to
specify these operators individually by name, as the <literal>SORT1</>,
<literal>SORT2</>, <literal>LTCMP</>, and <literal>GTCMP</> options
respectively. The system will fill in the default names
<literal>&lt;</>, <literal>&lt;</>, <literal>&lt;</>, <literal>&gt;</>
respectively if any of these are omitted when <literal>MERGES</> is
specified. Also, <literal>MERGES</> will be assumed to be implied if any
of these four operator options appear, so it is possible to specify
just some of them and let the system fill in the rest.
</para>
<para>
The operand data types of the four comparison operators can be deduced
from the operand types of the merge-joinable operator, so just as with
<literal>COMMUTATOR</>, only the operator names need be given in these
clauses. Unless you are using peculiar choices of operator names,
it's sufficient to write <literal>MERGES</> and let the system fill in
the details.
(As with <literal>COMMUTATOR</> and <literal>NEGATOR</>, the system is
able to make dummy
operator entries if you happen to define the equality operator before
the other ones.)
To be marked <literal>MERGES</literal>, the join operator must appear
in a btree index operator family. This is not enforced when you create
the operator, since of course the referencing operator family couldn't
exist yet. But the operator will not actually be used for merge joins
unless a matching operator family can be found. The
<literal>MERGES</literal> flag thus acts as a hint to the planner that
it's worth looking for a matching operator family.
</para>
<para>
@ -474,13 +451,6 @@ table1.column1 OP table2.column2
be transitive.
</para>
</listitem>
<listitem>
<para>
Bizarre results will ensue at run time if the four comparison
operators you name do not sort the data values compatibly.
</para>
</listitem>
</itemizedlist>
</para>
@ -491,17 +461,5 @@ table1.column1 OP table2.column2
attempt to use the operator for a merge join.
</para>
</note>
<note>
<para>
In <productname>PostgreSQL</productname> versions before 7.3,
the <literal>MERGES</> shorthand was not available: to make a
merge-joinable operator one had to write both <literal>SORT1</> and
<literal>SORT2</> explicitly. Also, the <literal>LTCMP</> and
<literal>GTCMP</>
options did not exist; the names of those operators were hardwired as
<literal>&lt;</> and <literal>&gt;</> respectively.
</para>
</note>
</sect2>
</sect1>