Commit a78fcfb5 authored by Tom Lane's avatar Tom Lane

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.
parent d31ccb6c
......@@ -459,9 +459,9 @@ AS
--GIN
--mark built-in gin's _int4_ops as non default
update pg_opclass set opcdefault = 'f' where
pg_opclass.opcamid = (select pg_am.oid from pg_am where amname='gin') and
opcname = '_int4_ops';
update pg_catalog.pg_opclass set opcdefault = 'f'
where opcmethod = (select oid from pg_catalog.pg_am where amname='gin') and
opcname = '_int4_ops';
CREATE FUNCTION ginint4_queryextract(internal, internal, int2)
RETURNS internal
......
......@@ -124,7 +124,7 @@ DROP FUNCTION querytree(query_int);
DROP TYPE query_int CASCADE;
update pg_opclass set opcdefault = 't' where
pg_opclass.opcamid = (select pg_am.oid from pg_am where amname='gin') and
opcname = '_int4_ops';
--mark built-in gin's _int4_ops as default again
update pg_catalog.pg_opclass set opcdefault = 't'
where opcmethod = (select oid from pg_catalog.pg_am where amname='gin') and
opcname = '_int4_ops';
This diff is collapsed.
<!-- $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
......
<!-- $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>
......
<!--
$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>
......
<!-- $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>
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/hash/hashfunc.c,v 1.48 2006/10/04 00:29:48 momjian Exp $
* $PostgreSQL: pgsql/src/backend/access/hash/hashfunc.c,v 1.49 2006/12/23 00:43:08 tgl Exp $
*
* NOTES
* These functions are stored in pg_amproc. For each operator class
......@@ -46,11 +46,11 @@ hashint8(PG_FUNCTION_ARGS)
{
/*
* The idea here is to produce a hash value compatible with the values
* produced by hashint4 and hashint2 for logically equivalent inputs; this
* is necessary if we ever hope to support cross-type hash joins across
* these input types. Since all three types are signed, we can xor the
* high half of the int8 value if the sign is positive, or the complement
* of the high half when the sign is negative.
* produced by hashint4 and hashint2 for logically equal inputs; this is
* necessary to support cross-type hash joins across these input types.
* Since all three types are signed, we can xor the high half of the int8
* value if the sign is positive, or the complement of the high half when
* the sign is negative.
*/
#ifndef INT64_IS_BUSTED
int64 val = PG_GETARG_INT64(0);
......@@ -76,16 +76,26 @@ Datum
hashfloat4(PG_FUNCTION_ARGS)
{
float4 key = PG_GETARG_FLOAT4(0);
float8 key8;
/*
* On IEEE-float machines, minus zero and zero have different bit patterns
* but should compare as equal. We must ensure that they have the same
* hash value, which is most easily done this way:
* hash value, which is most reliably done this way:
*/
if (key == (float4) 0)
PG_RETURN_UINT32(0);
return hash_any((unsigned char *) &key, sizeof(key));
/*
* To support cross-type hashing of float8 and float4, we want to return
* the same hash value hashfloat8 would produce for an equal float8 value.
* So, widen the value to float8 and hash that. (We must do this rather
* than have hashfloat8 try to narrow its value to float4; that could
* fail on overflow.)
*/
key8 = key;
return hash_any((unsigned char *) &key8, sizeof(key8));
}
Datum
......@@ -96,7 +106,7 @@ hashfloat8(PG_FUNCTION_ARGS)
/*
* On IEEE-float machines, minus zero and zero have different bit patterns
* but should compare as equal. We must ensure that they have the same
* hash value, which is most easily done this way:
* hash value, which is most reliably done this way:
*/
if (key == (float8) 0)
PG_RETURN_UINT32(0);
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.95 2006/10/04 00:29:48 momjian Exp $
* $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.96 2006/12/23 00:43:08 tgl Exp $
*
* INTERFACE ROUTINES
* index_open - open an index relation by relation OID
......@@ -608,17 +608,27 @@ index_vacuum_cleanup(IndexVacuumInfo *info,
/* ----------------
* index_getprocid
*
* Some indexed access methods may require support routines that are
* not in the operator class/operator model imposed by pg_am. These
* access methods may store the OIDs of registered procedures they
* need in pg_amproc. These registered procedure OIDs are ordered in
* a way that makes sense to the access method, and used only by the
* access method. The general index code doesn't know anything about
* the routines involved; it just builds an ordered list of them for
* Index access methods typically require support routines that are
* not directly the implementation of any WHERE-clause query operator
* and so cannot be kept in pg_amop. Instead, such routines are kept
* in pg_amproc. These registered procedure OIDs are assigned numbers
* according to a convention established by the access method.
* The general index code doesn't know anything about the routines
* involved; it just builds an ordered list of them for
* each attribute on which an index is defined.
*
* This routine returns the requested procedure OID for a particular
* indexed attribute.
* As of Postgres 8.3, support routines within an operator family
* are further subdivided by the "left type" and "right type" of the
* query operator(s) that they support. The "default" functions for a
* particular indexed attribute are those with both types equal to
* the index opclass' opcintype (note that this is subtly different
* from the indexed attribute's own type: it may be a binary-compatible
* type instead). Only the default functions are stored in relcache
* entries --- access methods can use the syscache to look up non-default
* functions.
*
* This routine returns the requested default procedure OID for a
* particular indexed attribute.
* ----------------
*/
RegProcedure
......@@ -647,7 +657,8 @@ index_getprocid(Relation irel,
* index_getprocinfo
*
* This routine allows index AMs to keep fmgr lookup info for
* support procs in the relcache.
* support procs in the relcache. As above, only the "default"
* functions for any particular indexed attribute are cached.
*
* Note: the return value points into cached data that will be lost during
* any relcache rebuild! Therefore, either use the callinfo right away,
......
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.107 2006/10/04 00:29:49 momjian Exp $
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.108 2006/12/23 00:43:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -658,11 +658,14 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* to an insertion scan key by replacing the sk_func with the
* appropriate btree comparison function.
*
* If scankey operator is of default subtype, we can use the
* cached comparison function; otherwise gotta look it up in the
* catalogs.
* If scankey operator is of the default type for the index, we
* can use the cached comparison function; otherwise gotta look it
* up in the catalogs. Also, we support the convention that
* sk_subtype == 0 means the default type; this is a hack to
* simplify life for ScanKeyInit().
*/
if (cur->sk_subtype == InvalidOid)
if (cur->sk_subtype == rel->rd_opcintype[i] ||
cur->sk_subtype == InvalidOid)
{
FmgrInfo *procinfo;
......@@ -671,7 +674,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
cur->sk_flags,
cur->sk_attno,
InvalidStrategy,
InvalidOid,
cur->sk_subtype,
procinfo,
cur->sk_argument);
}
......@@ -679,9 +682,14 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
{
RegProcedure cmp_proc;
cmp_proc = get_opclass_proc(rel->rd_indclass->values[i],
cur->sk_subtype,
BTORDER_PROC);
cmp_proc = get_opfamily_proc(rel->rd_opfamily[i],
rel->rd_opcintype[i],
cur->sk_subtype,
BTORDER_PROC);
if (!RegProcedureIsValid(cmp_proc))
elog(ERROR, "missing support function %d(%u,%u) for attribute %d of index \"%s\"",
BTORDER_PROC, rel->rd_opcintype[i], cur->sk_subtype,
cur->sk_attno, RelationGetRelationName(rel));
ScanKeyEntryInitialize(scankeys + i,
cur->sk_flags,
cur->sk_attno,
......
......@@ -2,7 +2,7 @@
#
# Makefile for backend/catalog
#
# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.60 2006/07/31 01:16:36 tgl Exp $
# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.61 2006/12/23 00:43:09 tgl Exp $
#
#-------------------------------------------------------------------------
......@@ -28,8 +28,8 @@ SUBSYS.o: $(OBJS)
POSTGRES_BKI_SRCS := $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_proc.h pg_type.h pg_attribute.h pg_class.h pg_autovacuum.h \
pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h \
pg_operator.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \
pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \
pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \
pg_namespace.h pg_conversion.h pg_depend.h \
......
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.60 2006/10/04 00:29:50 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.61 2006/12/23 00:43:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -22,6 +22,8 @@
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
......@@ -33,6 +35,7 @@
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_tablespace.h"
......@@ -78,19 +81,25 @@ typedef struct
* See also getObjectClass().
*/
static const Oid object_classes[MAX_OCLASS] = {
RelationRelationId, /* OCLASS_CLASS */
ProcedureRelationId, /* OCLASS_PROC */
TypeRelationId, /* OCLASS_TYPE */
CastRelationId, /* OCLASS_CAST */
ConstraintRelationId, /* OCLASS_CONSTRAINT */
ConversionRelationId, /* OCLASS_CONVERSION */
AttrDefaultRelationId, /* OCLASS_DEFAULT */
LanguageRelationId, /* OCLASS_LANGUAGE */
OperatorRelationId, /* OCLASS_OPERATOR */
OperatorClassRelationId, /* OCLASS_OPCLASS */
RewriteRelationId, /* OCLASS_REWRITE */
TriggerRelationId, /* OCLASS_TRIGGER */
NamespaceRelationId /* OCLASS_SCHEMA */
RelationRelationId, /* OCLASS_CLASS */
ProcedureRelationId, /* OCLASS_PROC */
TypeRelationId, /* OCLASS_TYPE */
CastRelationId, /* OCLASS_CAST */
ConstraintRelationId, /* OCLASS_CONSTRAINT */
ConversionRelationId, /* OCLASS_CONVERSION */
AttrDefaultRelationId, /* OCLASS_DEFAULT */
LanguageRelationId, /* OCLASS_LANGUAGE */
OperatorRelationId, /* OCLASS_OPERATOR */
OperatorClassRelationId, /* OCLASS_OPCLASS */
OperatorFamilyRelationId, /* OCLASS_OPFAMILY */
AccessMethodOperatorRelationId, /* OCLASS_AMOP */
AccessMethodProcedureRelationId, /* OCLASS_AMPROC */
RewriteRelationId, /* OCLASS_REWRITE */
TriggerRelationId, /* OCLASS_TRIGGER */
NamespaceRelationId, /* OCLASS_SCHEMA */
AuthIdRelationId, /* OCLASS_ROLE */
DatabaseRelationId, /* OCLASS_DATABASE */
TableSpaceRelationId /* OCLASS_TBLSPACE */
};
......@@ -122,6 +131,7 @@ static int object_address_comparator(const void *a, const void *b);
static void add_object_address(ObjectClass oclass, Oid objectId, int32 subId,
ObjectAddresses *addrs);
static void getRelationDescription(StringInfo buffer, Oid relid);
static void getOpFamilyDescription(StringInfo buffer, Oid opfid);
/*
......@@ -185,7 +195,7 @@ performDeletion(const ObjectAddress *object,
* filled with some objects. Also, the deleted objects are saved in the
* alreadyDeleted list.
*
* XXX performDeletion could be refactored to be a thin wrapper to this
* XXX performDeletion could be refactored to be a thin wrapper around this
* function.
*/
static void
......@@ -954,6 +964,18 @@ doDeletion(const ObjectAddress *object)
RemoveOpClassById(object->objectId);
break;
case OCLASS_OPFAMILY:
RemoveOpFamilyById(object->objectId);
break;
case OCLASS_AMOP:
RemoveAmOpEntryById(object->objectId);
break;
case OCLASS_AMPROC:
RemoveAmProcEntryById(object->objectId);
break;
case OCLASS_REWRITE:
RemoveRewriteRuleById(object->objectId);
break;
......@@ -966,6 +988,8 @@ doDeletion(const ObjectAddress *object)
RemoveSchemaById(object->objectId);
break;
/* OCLASS_ROLE, OCLASS_DATABASE, OCLASS_TBLSPACE not handled */
default:
elog(ERROR, "unrecognized object class: %u",
object->classId);
......@@ -1316,9 +1340,9 @@ find_expr_references_walker(Node *node,
add_object_address(OCLASS_OPERATOR, lfirst_oid(l), 0,
context->addrs);
}
foreach(l, rcexpr->opclasses)
foreach(l, rcexpr->opfamilies)
{
add_object_address(OCLASS_OPCLASS, lfirst_oid(l), 0,
add_object_address(OCLASS_OPFAMILY, lfirst_oid(l), 0,
context->addrs);
}
/* fall through to examine arguments */
......@@ -1623,6 +1647,18 @@ getObjectClass(const ObjectAddress *object)
Assert(object->objectSubId == 0);
return OCLASS_OPCLASS;
case OperatorFamilyRelationId:
Assert(object->objectSubId == 0);
return OCLASS_OPFAMILY;
case AccessMethodOperatorRelationId:
Assert(object->objectSubId == 0);
return OCLASS_AMOP;
case AccessMethodProcedureRelationId:
Assert(object->objectSubId == 0);
return OCLASS_AMPROC;
case RewriteRelationId:
Assert(object->objectSubId == 0);
return OCLASS_REWRITE;
......@@ -1856,11 +1892,11 @@ getObjectDescription(const ObjectAddress *object)
opcForm = (Form_pg_opclass) GETSTRUCT(opcTup);
amTup = SearchSysCache(AMOID,
ObjectIdGetDatum(opcForm->opcamid),
ObjectIdGetDatum(opcForm->opcmethod),
0, 0, 0);
if (!HeapTupleIsValid(amTup))
elog(ERROR, "cache lookup failed for access method %u",
opcForm->opcamid);
opcForm->opcmethod);
amForm = (Form_pg_am) GETSTRUCT(amTup);
/* Qualify the name if not visible in search path */
......@@ -1879,6 +1915,84 @@ getObjectDescription(const ObjectAddress *object)
break;
}
case OCLASS_OPFAMILY:
getOpFamilyDescription(&buffer, object->objectId);
break;
case OCLASS_AMOP:
{
Relation amopDesc;
ScanKeyData skey[1];
SysScanDesc amscan;
HeapTuple tup;
Form_pg_amop amopForm;
amopDesc = heap_open(AccessMethodOperatorRelationId,
AccessShareLock);
ScanKeyInit(&skey[0],
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
amscan = systable_beginscan(amopDesc, AccessMethodOperatorOidIndexId, true,
SnapshotNow, 1, skey);
tup = systable_getnext(amscan);
if (!HeapTupleIsValid(tup))
elog(ERROR, "could not find tuple for amop entry %u",
object->objectId);
amopForm = (Form_pg_amop) GETSTRUCT(tup);
appendStringInfo(&buffer, _("operator %d %s of "),
amopForm->amopstrategy,
format_operator(amopForm->amopopr));
getOpFamilyDescription(&buffer, amopForm->amopfamily);
systable_endscan(amscan);
heap_close(amopDesc, AccessShareLock);
break;
}
case OCLASS_AMPROC:
{
Relation amprocDesc;
ScanKeyData skey[1];
SysScanDesc amscan;
HeapTuple tup;
Form_pg_amproc amprocForm;
amprocDesc = heap_open(AccessMethodProcedureRelationId,
AccessShareLock);
ScanKeyInit(&skey[0],
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
amscan = systable_beginscan(amprocDesc, AccessMethodProcedureOidIndexId, true,
SnapshotNow, 1, skey);
tup = systable_getnext(amscan);
if (!HeapTupleIsValid(tup))
elog(ERROR, "could not find tuple for amproc entry %u",
object->objectId);
amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
appendStringInfo(&buffer, _("function %d %s of "),
amprocForm->amprocnum,
format_procedure(amprocForm->amproc));
getOpFamilyDescription(&buffer, amprocForm->amprocfamily);
systable_endscan(amscan);
heap_close(amprocDesc, AccessShareLock);
break;
}
case OCLASS_REWRITE:
{
Relation ruleDesc;
......@@ -2068,3 +2182,45 @@ getRelationDescription(StringInfo buffer, Oid relid)
ReleaseSysCache(relTup);
}
/*
* subroutine for getObjectDescription: describe an operator family
*/
static void
getOpFamilyDescription(StringInfo buffer, Oid opfid)
{
HeapTuple opfTup;
Form_pg_opfamily opfForm;
HeapTuple amTup;
Form_pg_am amForm;
char *nspname;
opfTup = SearchSysCache(OPFAMILYOID,
ObjectIdGetDatum(opfid),
0, 0, 0);
if (!HeapTupleIsValid(opfTup))
elog(ERROR, "cache lookup failed for opfamily %u", opfid);
opfForm = (Form_pg_opfamily) GETSTRUCT(opfTup);
amTup = SearchSysCache(AMOID,
ObjectIdGetDatum(opfForm->opfmethod),
0, 0, 0);
if (!HeapTupleIsValid(amTup))
elog(ERROR, "cache lookup failed for access method %u",
opfForm->opfmethod);
amForm = (Form_pg_am) GETSTRUCT(amTup);
/* Qualify the name if not visible in search path */
if (OpfamilyIsVisible(opfid))
nspname = NULL;
else
nspname = get_namespace_name(opfForm->opfnamespace);
appendStringInfo(buffer, _("operator family %s for access method %s"),
quote_qualified_identifier(nspname,
NameStr(opfForm->opfname)),
NameStr(amForm->amname));
ReleaseSysCache(amTup);
ReleaseSysCache(opfTup);
}
......@@ -13,7 +13,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.88 2006/10/04 00:29:50 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.89 2006/12/23 00:43:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -27,6 +27,7 @@
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
......@@ -1062,7 +1063,7 @@ OpclassIsVisible(Oid opcid)
*/
char *opcname = NameStr(opcform->opcname);
visible = (OpclassnameGetOpcid(opcform->opcamid, opcname) == opcid);
visible = (OpclassnameGetOpcid(opcform->opcmethod, opcname) == opcid);
}
ReleaseSysCache(opctup);
......@@ -1070,6 +1071,89 @@ OpclassIsVisible(Oid opcid)
return visible;
}
/*
* OpfamilynameGetOpfid
* Try to resolve an unqualified index opfamily name.
* Returns OID if opfamily found in search path, else InvalidOid.
*
* This is essentially the same as TypenameGetTypid, but we have to have
* an extra argument for the index AM OID.
*/
Oid
OpfamilynameGetOpfid(Oid amid, const char *opfname)
{
Oid opfid;
ListCell *l;
recomputeNamespacePath();
foreach(l, namespaceSearchPath)
{
Oid namespaceId = lfirst_oid(l);
opfid = GetSysCacheOid(OPFAMILYAMNAMENSP,
ObjectIdGetDatum(amid),
PointerGetDatum(opfname),
ObjectIdGetDatum(namespaceId),
0);
if (OidIsValid(opfid))
return opfid;
}
/* Not found in path */
return InvalidOid;
}
/*
* OpfamilyIsVisible
* Determine whether an opfamily (identified by OID) is visible in the
* current search path. Visible means "would be found by searching
* for the unqualified opfamily name".
*/
bool
OpfamilyIsVisible(Oid opfid)
{
HeapTuple opftup;
Form_pg_opfamily opfform;
Oid opfnamespace;
bool visible;
opftup = SearchSysCache(OPFAMILYOID,
ObjectIdGetDatum(opfid),
0, 0, 0);
if (!HeapTupleIsValid(opftup))
elog(ERROR, "cache lookup failed for opfamily %u", opfid);
opfform = (Form_pg_opfamily) GETSTRUCT(opftup);
recomputeNamespacePath();
/*
* Quick check: if it ain't in the path at all, it ain't visible. Items in
* the system namespace are surely in the path and so we needn't even do
* list_member_oid() for them.
*/
opfnamespace = opfform->opfnamespace;
if (opfnamespace != PG_CATALOG_NAMESPACE &&
!list_member_oid(namespaceSearchPath, opfnamespace))
visible = false;
else
{
/*
* If it is in the path, it might still not be visible; it could be
* hidden by another opfamily of the same name earlier in the path. So
* we must do a slow check to see if this opfamily would be found by
* OpfamilynameGetOpfid.
*/
char *opfname = NameStr(opfform->opfname);
visible = (OpfamilynameGetOpfid(opfform->opfmethod, opfname) == opfid);
}
ReleaseSysCache(opftup);
return visible;
}
/*
* ConversionGetConid
* Try to resolve an unqualified conversion name.
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_operator.c,v 1.98 2006/07/14 14:52:18 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_operator.c,v 1.99 2006/12/23 00:43:09 tgl Exp $
*
* NOTES
* these routines moved here from commands/define.c and somewhat cleaned up.
......@@ -238,16 +238,13 @@ OperatorShellMake(const char *operatorName,
values[i++] = ObjectIdGetDatum(operatorNamespace); /* oprnamespace */
values[i++] = ObjectIdGetDatum(GetUserId()); /* oprowner */
values[i++] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l'); /* oprkind */
values[i++] = BoolGetDatum(false); /* oprcanmerge */
values[i++] = BoolGetDatum(false); /* oprcanhash */
values[i++] = ObjectIdGetDatum(leftTypeId); /* oprleft */
values[i++] = ObjectIdGetDatum(rightTypeId); /* oprright */
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprresult */
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprcom */
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprnegate */
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprlsortop */
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprrsortop */
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprltcmpop */
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprgtcmpop */
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprcode */
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprrest */
values[i++] = ObjectIdGetDatum(InvalidOid); /* oprjoin */
......@@ -296,11 +293,8 @@ OperatorShellMake(const char *operatorName,
* negatorName X negator operator
* restrictionName X restriction sel. procedure
* joinName X join sel. procedure
* canMerge merge join can be used with this operator
* canHash hash join can be used with this operator
* leftSortName X left sort operator (for merge join)
* rightSortName X right sort operator (for merge join)
* ltCompareName X L<R compare operator (for merge join)
* gtCompareName X L>R compare operator (for merge join)
*
* This routine gets complicated because it allows the user to
* specify operators that do not exist. For example, if operator
......@@ -326,6 +320,7 @@ OperatorShellMake(const char *operatorName,
* operatorName
* owner id (simply the user id of the caller)
* operator "kind" either "b" for binary or "l" for left unary
* canMerge boolean
* canHash boolean
* leftTypeObjectId -- type must already be defined
* rightTypeObjectId -- this is optional, enter ObjectId=0 if none specified
......@@ -341,8 +336,6 @@ OperatorShellMake(const char *operatorName,
* (We are creating a self-commutating operator.)
* The link will be fixed later by OperatorUpd.
* negatorObjectId -- same as for commutatorObjectId
* leftSortObjectId -- same as for commutatorObjectId
* rightSortObjectId -- same as for commutatorObjectId
* operatorProcedure -- must access the pg_procedure catalog to get the
* ObjectId of the procedure that actually does the operator
* actions this is required. Do a lookup to find out the
......@@ -369,11 +362,8 @@ OperatorCreate(const char *operatorName,
List *negatorName,
List *restrictionName,
List *joinName,
bool canHash,
List *leftSortName,
List *rightSortName,
List *ltCompareName,
List *gtCompareName)
bool canMerge,
bool canHash)
{
Relation pg_operator_desc;
HeapTuple tup;
......@@ -386,10 +376,6 @@ OperatorCreate(const char *operatorName,
Oid operResultType;
Oid commutatorId,
negatorId,
leftSortId,
rightSortId,
ltCompareId,
gtCompareId,
restOid,
joinOid;
bool selfCommutator = false;
......@@ -424,14 +410,14 @@ OperatorCreate(const char *operatorName,
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("only binary operators can have join selectivity")));
if (canHash)
if (canMerge)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("only binary operators can hash")));
if (leftSortName || rightSortName || ltCompareName || gtCompareName)
errmsg("only binary operators can merge join")));
if (canHash)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("only binary operators can merge join")));
errmsg("only binary operators can hash")));
}
operatorObjectId = OperatorGet(operatorName,
......@@ -522,6 +508,7 @@ OperatorCreate(const char *operatorName,
values[i++] = ObjectIdGetDatum(operatorNamespace); /* oprnamespace */
values[i++] = ObjectIdGetDatum(GetUserId()); /* oprowner */
values[i++] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l'); /* oprkind */
values[i++] = BoolGetDatum(canMerge); /* oprcanmerge */
values[i++] = BoolGetDatum(canHash); /* oprcanhash */
values[i++] = ObjectIdGetDatum(leftTypeId); /* oprleft */
values[i++] = ObjectIdGetDatum(rightTypeId); /* oprright */
......@@ -565,58 +552,6 @@ OperatorCreate(const char *operatorName,
negatorId = InvalidOid;
values[i++] = ObjectIdGetDatum(negatorId); /* oprnegate */
if (leftSortName)
{
/* left sort op takes left-side data type */
leftSortId = get_other_operator(leftSortName,
leftTypeId, leftTypeId,
operatorName, operatorNamespace,
leftTypeId, rightTypeId,
false);
}
else
leftSortId = InvalidOid;
values[i++] = ObjectIdGetDatum(leftSortId); /* oprlsortop */
if (rightSortName)
{
/* right sort op takes right-side data type */
rightSortId = get_other_operator(rightSortName,
rightTypeId, rightTypeId,
operatorName, operatorNamespace,
leftTypeId, rightTypeId,
false);
}
else
rightSortId = InvalidOid;
values[i++] = ObjectIdGetDatum(rightSortId); /* oprrsortop */
if (ltCompareName)
{
/* comparator has same arg types */
ltCompareId = get_other_operator(ltCompareName,
leftTypeId, rightTypeId,
operatorName, operatorNamespace,
leftTypeId, rightTypeId,
false);
}
else
ltCompareId = InvalidOid;
values[i++] = ObjectIdGetDatum(ltCompareId); /* oprltcmpop */
if (gtCompareName)
{
/* comparator has same arg types */
gtCompareId = get_other_operator(gtCompareName,
leftTypeId, rightTypeId,
operatorName, operatorNamespace,
leftTypeId, rightTypeId,
false);
}
else
gtCompareId = InvalidOid;
values[i++] = ObjectIdGetDatum(gtCompareId); /* oprgtcmpop */
values[i++] = ObjectIdGetDatum(procOid); /* oprcode */
values[i++] = ObjectIdGetDatum(restOid); /* oprrest */
values[i++] = ObjectIdGetDatum(joinOid); /* oprjoin */
......@@ -930,12 +865,11 @@ makeOperatorDependencies(HeapTuple tuple)
/*
* NOTE: we do not consider the operator to depend on the associated
* operators oprcom, oprnegate, oprlsortop, oprrsortop, oprltcmpop,
* oprgtcmpop. We would not want to delete this operator if those go
* away, but only reset the link fields; which is not a function that the
* dependency code can presently handle. (Something could perhaps be done
* with objectSubId though.) For now, it's okay to let those links dangle
* if a referenced operator is removed.
* operators oprcom and oprnegate. We would not want to delete this
* operator if those go away, but only reset the link fields; which is not
* a function that the dependency code can presently handle. (Something
* could perhaps be done with objectSubId though.) For now, it's okay to
* let those links dangle if a referenced operator is removed.
*/
/* Dependency on implementation function */
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.149 2006/10/04 00:29:51 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.150 2006/12/23 00:43:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -802,31 +802,37 @@ GetIndexOpClass(List *opclass, Oid attrType,
Oid
GetDefaultOpClass(Oid type_id, Oid am_id)
{
Oid result = InvalidOid;
int nexact = 0;
int ncompatible = 0;
Oid exactOid = InvalidOid;
Oid compatibleOid = InvalidOid;
int ncompatiblepreferred = 0;
Relation rel;
ScanKeyData skey[1];
SysScanDesc scan;
HeapTuple tup;
CATEGORY tcategory;
/* If it's a domain, look at the base type instead */
type_id = getBaseType(type_id);
tcategory = TypeCategory(type_id);
/*
* We scan through all the opclasses available for the access method,
* looking for one that is marked default and matches the target type
* (either exactly or binary-compatibly, but prefer an exact match).
*
* We could find more than one binary-compatible match, in which case we
* require the user to specify which one he wants. If we find more than
* one exact match, then someone put bogus entries in pg_opclass.
* We could find more than one binary-compatible match. If just one is
* for a preferred type, use that one; otherwise we fail, forcing the user
* to specify which one he wants. (The preferred-type special case is a
* kluge for varchar: it's binary-compatible to both text and bpchar, so
* we need a tiebreaker.) If we find more than one exact match, then
* someone put bogus entries in pg_opclass.
*/
rel = heap_open(OperatorClassRelationId, AccessShareLock);
ScanKeyInit(&skey[0],
Anum_pg_opclass_opcamid,
Anum_pg_opclass_opcmethod,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(am_id));
......@@ -837,17 +843,26 @@ GetDefaultOpClass(Oid type_id, Oid am_id)
{
Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
if (opclass->opcdefault)
/* ignore altogether if not a default opclass */
if (!opclass->opcdefault)
continue;
if (opclass->opcintype == type_id)
{
if (opclass->opcintype == type_id)
nexact++;
result = HeapTupleGetOid(tup);
}
else if (nexact == 0 &&
IsBinaryCoercible(type_id, opclass->opcintype))
{
if (IsPreferredType(tcategory, opclass->opcintype))
{
nexact++;
exactOid = HeapTupleGetOid(tup);
ncompatiblepreferred++;
result = HeapTupleGetOid(tup);
}
else if (IsBinaryCoercible(type_id, opclass->opcintype))
else if (ncompatiblepreferred == 0)
{
ncompatible++;
compatibleOid = HeapTupleGetOid(tup);
result = HeapTupleGetOid(tup);
}
}
}
......@@ -856,15 +871,17 @@ GetDefaultOpClass(Oid type_id, Oid am_id)
heap_close(rel, AccessShareLock);
if (nexact == 1)
return exactOid;
if (nexact != 0)
/* raise error if pg_opclass contains inconsistent data */
if (nexact > 1)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("there are multiple default operator classes for data type %s",
format_type_be(type_id))));
if (ncompatible == 1)
return compatibleOid;
if (nexact == 1 ||
ncompatiblepreferred == 1 ||
(ncompatiblepreferred == 0 && ncompatible == 1))
return result;
return InvalidOid;
}
......
This diff is collapsed.
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/operatorcmds.c,v 1.33 2006/10/04 00:29:51 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/operatorcmds.c,v 1.34 2006/12/23 00:43:09 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
......@@ -64,8 +64,8 @@ DefineOperator(List *names, List *parameters)
char *oprName;
Oid oprNamespace;
AclResult aclresult;
bool canHash = false; /* operator hashes */
bool canMerge = false; /* operator merges */
bool canHash = false; /* operator hashes */
List *functionName = NIL; /* function for operator */
TypeName *typeName1 = NULL; /* first type name */
TypeName *typeName2 = NULL; /* second type name */
......@@ -75,10 +75,6 @@ DefineOperator(List *names, List *parameters)
List *negatorName = NIL; /* optional negator operator name */
List *restrictionName = NIL; /* optional restrict. sel. procedure */
List *joinName = NIL; /* optional join sel. procedure */
List *leftSortName = NIL; /* optional left sort operator */
List *rightSortName = NIL; /* optional right sort operator */
List *ltCompareName = NIL; /* optional < compare operator */
List *gtCompareName = NIL; /* optional > compare operator */
ListCell *pl;
/* Convert list of names to a name and namespace */
......@@ -127,14 +123,15 @@ DefineOperator(List *names, List *parameters)
canHash = defGetBoolean(defel);
else if (pg_strcasecmp(defel->defname, "merges") == 0)
canMerge = defGetBoolean(defel);
/* These obsolete options are taken as meaning canMerge */
else if (pg_strcasecmp(defel->defname, "sort1") == 0)
leftSortName = defGetQualifiedName(defel);
canMerge = true;
else if (pg_strcasecmp(defel->defname, "sort2") == 0)
rightSortName = defGetQualifiedName(defel);
canMerge = true;
else if (pg_strcasecmp(defel->defname, "ltcmp") == 0)
ltCompareName = defGetQualifiedName(defel);
canMerge = true;
else if (pg_strcasecmp(defel->defname, "gtcmp") == 0)
gtCompareName = defGetQualifiedName(defel);
canMerge = true;
else
ereport(WARNING,
(errcode(ERRCODE_SYNTAX_ERROR),
......@@ -156,26 +153,6 @@ DefineOperator(List *names, List *parameters)
if (typeName2)
typeId2 = typenameTypeId(NULL, typeName2);
/*
* If any of the mergejoin support operators were given, then canMerge is
* implicit. If canMerge is specified or implicit, fill in default
* operator names for any missing mergejoin support operators.
*/
if (leftSortName || rightSortName || ltCompareName || gtCompareName)
canMerge = true;
if (canMerge)
{
if (!leftSortName)
leftSortName = list_make1(makeString("<"));
if (!rightSortName)
rightSortName = list_make1(makeString("<"));
if (!ltCompareName)
ltCompareName = list_make1(makeString("<"));
if (!gtCompareName)
gtCompareName = list_make1(makeString(">"));
}
/*
* now have OperatorCreate do all the work..
*/
......@@ -188,11 +165,8 @@ DefineOperator(List *names, List *parameters)
negatorName, /* optional negator operator name */
restrictionName, /* optional restrict. sel. procedure */
joinName, /* optional join sel. procedure name */
canHash, /* operator hashes */
leftSortName, /* optional left sort operator */
rightSortName, /* optional right sort operator */
ltCompareName, /* optional < comparison op */
gtCompareName); /* optional < comparison op */
canMerge, /* operator merges */
canHash); /* operator hashes */
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.206 2006/10/13 21:43:18 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.207 2006/12/23 00:43:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -4145,7 +4145,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* generate a warning if not, since otherwise costly seqscans will be
* incurred to check FK validity.
*/
if (!op_in_opclass(oprid(o), opclasses[i]))
if (!op_in_opfamily(oprid(o), get_opclass_family(opclasses[i])))
ereport(WARNING,
(errmsg("foreign key constraint \"%s\" "
"will require costly sequential scans",
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.200 2006/12/21 16:05:13 petere Exp $
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.201 2006/12/23 00:43:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -3700,21 +3700,28 @@ ExecInitExpr(Expr *node, PlanState *parent)
outlist = lappend(outlist, estate);
}
rstate->rargs = outlist;
Assert(list_length(rcexpr->opclasses) == nopers);
Assert(list_length(rcexpr->opfamilies) == nopers);
rstate->funcs = (FmgrInfo *) palloc(nopers * sizeof(FmgrInfo));
i = 0;
forboth(l, rcexpr->opnos, l2, rcexpr->opclasses)
forboth(l, rcexpr->opnos, l2, rcexpr->opfamilies)
{
Oid opno = lfirst_oid(l);
Oid opclass = lfirst_oid(l2);
Oid opfamily = lfirst_oid(l2);
int strategy;
Oid subtype;
Oid lefttype;
Oid righttype;
bool recheck;
Oid proc;
get_op_opclass_properties(opno, opclass,
&strategy, &subtype, &recheck);
proc = get_opclass_proc(opclass, subtype, BTORDER_PROC);
get_op_opfamily_properties(opno, opfamily,
&strategy,
&lefttype,
&righttype,
&recheck);
proc = get_opfamily_proc(opfamily,
lefttype,
righttype,
BTORDER_PROC);
/*
* If we enforced permissions checks on index support
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.117 2006/10/04 00:29:52 momjian Exp $
* $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.118 2006/12/23 00:43:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -797,9 +797,10 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
int flags = SK_ROW_MEMBER;
Datum scanvalue;
Oid opno;
Oid opclass;
Oid opfamily;
int op_strategy;
Oid op_subtype;
Oid op_lefttype;
Oid op_righttype;
bool op_recheck;
/*
......@@ -857,15 +858,21 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
if (index->rd_rel->relam != BTREE_AM_OID ||
varattno < 1 || varattno > index->rd_index->indnatts)
elog(ERROR, "bogus RowCompare index qualification");
opclass = index->rd_indclass->values[varattno - 1];
opfamily = index->rd_opfamily[varattno - 1];
get_op_opclass_properties(opno, opclass,
&op_strategy, &op_subtype, &op_recheck);
get_op_opfamily_properties(opno, opfamily,
&op_strategy,
&op_lefttype,
&op_righttype,
&op_recheck);
if (op_strategy != rc->rctype)
elog(ERROR, "RowCompare index qualification contains wrong operator");
opfuncid = get_opclass_proc(opclass, op_subtype, BTORDER_PROC);
opfuncid = get_opfamily_proc(opfamily,
op_lefttype,
op_righttype,
BTORDER_PROC);
/*
* initialize the subsidiary scan key's fields appropriately
......@@ -874,7 +881,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
flags,
varattno, /* attribute number */
op_strategy, /* op's strategy */
op_subtype, /* strategy subtype */
op_righttype, /* strategy subtype */
opfuncid, /* reg proc to use */
scanvalue); /* constant */
extra_scan_keys++;
......
This diff is collapsed.
......@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.355 2006/12/21 16:05:13 petere Exp $
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.356 2006/12/23 00:43:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -449,6 +449,8 @@ _copyMergeJoin(MergeJoin *from)
* copy remainder of node
*/
COPY_NODE_FIELD(mergeclauses);
COPY_NODE_FIELD(mergefamilies);
COPY_NODE_FIELD(mergestrategies);
return newnode;
}
......@@ -1055,7 +1057,7 @@ _copyRowCompareExpr(RowCompareExpr *from)
COPY_SCALAR_FIELD(rctype);
COPY_NODE_FIELD(opnos);
COPY_NODE_FIELD(opclasses);
COPY_NODE_FIELD(opfamilies);
COPY_NODE_FIELD(largs);
COPY_NODE_FIELD(rargs);
......@@ -1307,6 +1309,7 @@ _copyRestrictInfo(RestrictInfo *from)
COPY_SCALAR_FIELD(mergejoinoperator);
COPY_SCALAR_FIELD(left_sortop);
COPY_SCALAR_FIELD(right_sortop);
COPY_SCALAR_FIELD(mergeopfamily);
/*
* Do not copy pathkeys, since they'd not be canonical in a copied query
......@@ -2291,6 +2294,7 @@ _copyCreateOpClassStmt(CreateOpClassStmt *from)
CreateOpClassStmt *newnode = makeNode(CreateOpClassStmt);
COPY_NODE_FIELD(opclassname);
COPY_NODE_FIELD(opfamilyname);
COPY_STRING_FIELD(amname);
COPY_NODE_FIELD(datatype);
COPY_NODE_FIELD(items);
......
......@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.289 2006/12/21 16:05:13 petere Exp $
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.290 2006/12/23 00:43:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -428,7 +428,7 @@ _equalRowCompareExpr(RowCompareExpr *a, RowCompareExpr *b)
{
COMPARE_SCALAR_FIELD(rctype);
COMPARE_NODE_FIELD(opnos);
COMPARE_NODE_FIELD(opclasses);
COMPARE_NODE_FIELD(opfamilies);
COMPARE_NODE_FIELD(largs);
COMPARE_NODE_FIELD(rargs);
......@@ -1163,6 +1163,7 @@ static bool
_equalCreateOpClassStmt(CreateOpClassStmt *a, CreateOpClassStmt *b)
{
COMPARE_NODE_FIELD(opclassname);
COMPARE_NODE_FIELD(opfamilyname);
COMPARE_STRING_FIELD(amname);
COMPARE_NODE_FIELD(datatype);
COMPARE_NODE_FIELD(items);
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.287 2006/12/21 16:05:13 petere Exp $
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.288 2006/12/23 00:43:10 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
......@@ -442,6 +442,8 @@ _outMergeJoin(StringInfo str, MergeJoin *node)
_outJoinPlanInfo(str, (Join *) node);
WRITE_NODE_FIELD(mergeclauses);
WRITE_NODE_FIELD(mergefamilies);
WRITE_NODE_FIELD(mergestrategies);
}
static void
......@@ -866,7 +868,7 @@ _outRowCompareExpr(StringInfo str, RowCompareExpr *node)
WRITE_ENUM_FIELD(rctype, RowCompareType);
WRITE_NODE_FIELD(opnos);
WRITE_NODE_FIELD(opclasses);
WRITE_NODE_FIELD(opfamilies);
WRITE_NODE_FIELD(largs);
WRITE_NODE_FIELD(rargs);
}
......@@ -1167,6 +1169,8 @@ _outMergePath(StringInfo str, MergePath *node)
_outJoinPathInfo(str, (JoinPath *) node);
WRITE_NODE_FIELD(path_mergeclauses);
WRITE_NODE_FIELD(path_mergefamilies);
WRITE_NODE_FIELD(path_mergestrategies);
WRITE_NODE_FIELD(outersortkeys);
WRITE_NODE_FIELD(innersortkeys);
}
......@@ -1281,6 +1285,7 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
WRITE_OID_FIELD(mergejoinoperator);
WRITE_OID_FIELD(left_sortop);
WRITE_OID_FIELD(right_sortop);
WRITE_OID_FIELD(mergeopfamily);
WRITE_NODE_FIELD(left_pathkey);
WRITE_NODE_FIELD(right_pathkey);
WRITE_OID_FIELD(hashjoinoperator);
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.197 2006/12/21 16:05:13 petere Exp $
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.198 2006/12/23 00:43:10 tgl Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
......@@ -672,7 +672,7 @@ _readRowCompareExpr(void)
READ_ENUM_FIELD(rctype, RowCompareType);
READ_NODE_FIELD(opnos);
READ_NODE_FIELD(opclasses);
READ_NODE_FIELD(opfamilies);
READ_NODE_FIELD(largs);
READ_NODE_FIELD(rargs);
......
......@@ -54,7 +54,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.170 2006/12/15 18:42:26 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.171 2006/12/23 00:43:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1258,6 +1258,8 @@ cost_mergejoin(MergePath *path, PlannerInfo *root)
Path *outer_path = path->jpath.outerjoinpath;
Path *inner_path = path->jpath.innerjoinpath;
List *mergeclauses = path->path_mergeclauses;
List *mergefamilies = path->path_mergefamilies;
List *mergestrategies = path->path_mergestrategies;
List *outersortkeys = path->outersortkeys;
List *innersortkeys = path->innersortkeys;
Cost startup_cost = 0;
......@@ -1347,13 +1349,16 @@ cost_mergejoin(MergePath *path, PlannerInfo *root)
*
* Since this calculation is somewhat expensive, and will be the same for
* all mergejoin paths associated with the merge clause, we cache the
* results in the RestrictInfo node.
* results in the RestrictInfo node. XXX that won't work anymore once
* we support multiple possible orderings!
*/
if (mergeclauses && path->jpath.jointype != JOIN_FULL)
{
firstclause = (RestrictInfo *) linitial(mergeclauses);
if (firstclause->left_mergescansel < 0) /* not computed yet? */
mergejoinscansel(root, (Node *) firstclause->clause,
linitial_oid(mergefamilies),
linitial_int(mergestrategies),
&firstclause->left_mergescansel,
&firstclause->right_mergescansel);
......
This diff is collapsed.
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.107 2006/10/04 00:29:54 momjian Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.108 2006/12/23 00:43:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -16,6 +16,7 @@
#include <math.h>
#include "access/skey.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
......@@ -39,6 +40,8 @@ static List *select_mergejoin_clauses(RelOptInfo *joinrel,
RelOptInfo *innerrel,
List *restrictlist,
JoinType jointype);
static void build_mergejoin_strat_lists(List *mergeclauses,
List **mergefamilies, List **mergestrategies);
/*
......@@ -225,6 +228,8 @@ sort_inner_and_outer(PlannerInfo *root,
List *front_pathkey = (List *) lfirst(l);
List *cur_pathkeys;
List *cur_mergeclauses;
List *mergefamilies;
List *mergestrategies;
List *outerkeys;
List *innerkeys;
List *merge_pathkeys;
......@@ -269,6 +274,10 @@ sort_inner_and_outer(PlannerInfo *root,
merge_pathkeys = build_join_pathkeys(root, joinrel, jointype,
outerkeys);
/* Build opfamily info for execution */
build_mergejoin_strat_lists(cur_mergeclauses,
&mergefamilies, &mergestrategies);
/*
* And now we can make the path.
*/
......@@ -281,6 +290,8 @@ sort_inner_and_outer(PlannerInfo *root,
restrictlist,
merge_pathkeys,
cur_mergeclauses,
mergefamilies,
mergestrategies,
outerkeys,
innerkeys));
}
......@@ -410,6 +421,8 @@ match_unsorted_outer(PlannerInfo *root,
Path *outerpath = (Path *) lfirst(l);
List *merge_pathkeys;
List *mergeclauses;
List *mergefamilies;
List *mergestrategies;
List *innersortkeys;
List *trialsortkeys;
Path *cheapest_startup_inner;
......@@ -516,6 +529,10 @@ match_unsorted_outer(PlannerInfo *root,
mergeclauses,
innerrel);
/* Build opfamily info for execution */
build_mergejoin_strat_lists(mergeclauses,
&mergefamilies, &mergestrategies);
/*
* Generate a mergejoin on the basis of sorting the cheapest inner.
* Since a sort will be needed, only cheapest total cost matters. (But
......@@ -531,6 +548,8 @@ match_unsorted_outer(PlannerInfo *root,
restrictlist,
merge_pathkeys,
mergeclauses,
mergefamilies,
mergestrategies,
NIL,
innersortkeys));
......@@ -589,6 +608,11 @@ match_unsorted_outer(PlannerInfo *root,
}
else
newclauses = mergeclauses;
/* Build opfamily info for execution */
build_mergejoin_strat_lists(newclauses,
&mergefamilies, &mergestrategies);
add_path(joinrel, (Path *)
create_mergejoin_path(root,
joinrel,
......@@ -598,6 +622,8 @@ match_unsorted_outer(PlannerInfo *root,
restrictlist,
merge_pathkeys,
newclauses,
mergefamilies,
mergestrategies,
NIL,
NIL));
cheapest_total_inner = innerpath;
......@@ -633,6 +659,11 @@ match_unsorted_outer(PlannerInfo *root,
else
newclauses = mergeclauses;
}
/* Build opfamily info for execution */
build_mergejoin_strat_lists(newclauses,
&mergefamilies, &mergestrategies);
add_path(joinrel, (Path *)
create_mergejoin_path(root,
joinrel,
......@@ -642,6 +673,8 @@ match_unsorted_outer(PlannerInfo *root,
restrictlist,
merge_pathkeys,
newclauses,
mergefamilies,
mergestrategies,
NIL,
NIL));
}
......@@ -946,3 +979,35 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
return result_list;
}
/*
* Temporary hack to build opfamily and strategy lists needed for mergejoin
* by the executor. We need to rethink the planner's handling of merge
* planning so that it can deal with multiple possible merge orders, but
* that's not done yet.
*/
static void
build_mergejoin_strat_lists(List *mergeclauses,
List **mergefamilies, List **mergestrategies)
{
ListCell *l;
*mergefamilies = NIL;
*mergestrategies = NIL;
foreach(l, mergeclauses)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);
/*
* We do not need to worry about whether the mergeclause will be
* commuted at runtime --- it's the same opfamily either way.
*/
*mergefamilies = lappend_oid(*mergefamilies, restrictinfo->mergeopfamily);
/*
* For the moment, strategy must always be LessThan --- see
* hack version of get_op_mergejoin_info
*/
*mergestrategies = lappend_int(*mergestrategies, BTLessStrategyNumber);
}
}
......@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.217 2006/10/04 00:29:54 momjian Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.218 2006/12/23 00:43:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -73,7 +73,7 @@ static void fix_indexqual_references(List *indexquals, IndexPath *index_path,
List **indexstrategy,
List **indexsubtype);
static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index,
Oid *opclass);
Oid *opfamily);
static List *get_switched_clauses(List *clauses, Relids outerrelids);
static List *order_qual_clauses(PlannerInfo *root, List *clauses);
static void copy_path_costsize(Plan *dest, Path *src);
......@@ -113,7 +113,7 @@ static HashJoin *make_hashjoin(List *tlist,
static Hash *make_hash(Plan *lefttree);
static MergeJoin *make_mergejoin(List *tlist,
List *joinclauses, List *otherclauses,
List *mergeclauses,
List *mergeclauses, List *mergefamilies, List *mergestrategies,
Plan *lefttree, Plan *righttree,
JoinType jointype);
static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
......@@ -1540,6 +1540,8 @@ create_mergejoin_plan(PlannerInfo *root,
joinclauses,
otherclauses,
mergeclauses,
best_path->path_mergefamilies,
best_path->path_mergestrategies,
outer_plan,
inner_plan,
best_path->jpath.jointype);
......@@ -1676,9 +1678,10 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
Expr *clause;
Oid clause_op;
Oid opclass;
Oid opfamily;
int stratno;
Oid stratsubtype;
Oid stratlefttype;
Oid stratrighttype;
bool recheck;
Assert(IsA(rinfo, RestrictInfo));
......@@ -1709,11 +1712,11 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
/*
* Now, determine which index attribute this is, change the
* indexkey operand as needed, and get the index opclass.
* indexkey operand as needed, and get the index opfamily.
*/
linitial(op->args) = fix_indexqual_operand(linitial(op->args),
index,
&opclass);
&opfamily);
clause_op = op->opno;
}
else if (IsA(clause, RowCompareExpr))
......@@ -1734,20 +1737,20 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
* For each column in the row comparison, determine which index
* attribute this is and change the indexkey operand as needed.
*
* Save the index opclass for only the first column. We will
* return the operator and opclass info for just the first column
* Save the index opfamily for only the first column. We will
* return the operator and opfamily info for just the first column
* of the row comparison; the executor will have to look up the
* rest if it needs them.
*/
foreach(lc, rc->largs)
{
Oid tmp_opclass;
Oid tmp_opfamily;
lfirst(lc) = fix_indexqual_operand(lfirst(lc),
index,
&tmp_opclass);
&tmp_opfamily);
if (lc == list_head(rc->largs))
opclass = tmp_opclass;
opfamily = tmp_opfamily;
}
clause_op = linitial_oid(rc->opnos);
}
......@@ -1759,11 +1762,11 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
/*
* Now, determine which index attribute this is, change the
* indexkey operand as needed, and get the index opclass.
* indexkey operand as needed, and get the index opfamily.
*/
linitial(saop->args) = fix_indexqual_operand(linitial(saop->args),
index,
&opclass);
&opfamily);
clause_op = saop->opno;
}
else
......@@ -1776,15 +1779,18 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
*fixed_indexquals = lappend(*fixed_indexquals, clause);
/*
* Look up the (possibly commuted) operator in the operator class to
* get its strategy numbers and the recheck indicator. This also
* Look up the (possibly commuted) operator in the operator family to
* get its strategy number and the recheck indicator. This also
* double-checks that we found an operator matching the index.
*/
get_op_opclass_properties(clause_op, opclass,
&stratno, &stratsubtype, &recheck);
get_op_opfamily_properties(clause_op, opfamily,
&stratno,
&stratlefttype,
&stratrighttype,
&recheck);
*indexstrategy = lappend_int(*indexstrategy, stratno);
*indexsubtype = lappend_oid(*indexsubtype, stratsubtype);
*indexsubtype = lappend_oid(*indexsubtype, stratrighttype);
/* If it's not lossy, add to nonlossy_indexquals */
if (!recheck)
......@@ -1793,7 +1799,7 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
}
static Node *
fix_indexqual_operand(Node *node, IndexOptInfo *index, Oid *opclass)
fix_indexqual_operand(Node *node, IndexOptInfo *index, Oid *opfamily)
{
/*
* We represent index keys by Var nodes having the varno of the base table
......@@ -1826,8 +1832,8 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, Oid *opclass)
{
result = (Var *) copyObject(node);
result->varattno = pos + 1;
/* return the correct opclass, too */
*opclass = index->classlist[pos];
/* return the correct opfamily, too */
*opfamily = index->opfamily[pos];
return (Node *) result;
}
}
......@@ -1853,8 +1859,8 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, Oid *opclass)
result = makeVar(index->rel->relid, pos + 1,
exprType(lfirst(indexpr_item)), -1,
0);
/* return the correct opclass, too */
*opclass = index->classlist[pos];
/* return the correct opfamily, too */
*opfamily = index->opfamily[pos];
return (Node *) result;
}
indexpr_item = lnext(indexpr_item);
......@@ -1863,7 +1869,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, Oid *opclass)
/* Ooops... */
elog(ERROR, "node is not an index attribute");
*opclass = InvalidOid; /* keep compiler quiet */
*opfamily = InvalidOid; /* keep compiler quiet */
return NULL;
}
......@@ -2327,6 +2333,8 @@ make_mergejoin(List *tlist,
List *joinclauses,
List *otherclauses,
List *mergeclauses,
List *mergefamilies,
List *mergestrategies,
Plan *lefttree,
Plan *righttree,
JoinType jointype)
......@@ -2340,6 +2348,8 @@ make_mergejoin(List *tlist,
plan->lefttree = lefttree;
plan->righttree = righttree;
node->mergeclauses = mergeclauses;
node->mergefamilies = mergefamilies;
node->mergestrategies = mergestrategies;
node->join.jointype = jointype;
node->join.joinqual = joinclauses;
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.124 2006/12/07 19:33:40 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.125 2006/12/23 00:43:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1109,10 +1109,10 @@ process_implied_equality(PlannerInfo *root,
/*
* Let's just make sure this appears to be a compatible operator.
*
* XXX needs work
*/
if (pgopform->oprlsortop != sortop1 ||
pgopform->oprrsortop != sortop2 ||
pgopform->oprresult != BOOLOID)
if (pgopform->oprresult != BOOLOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("equality operator for types %s and %s should be merge-joinable, but isn't",
......@@ -1276,6 +1276,7 @@ check_mergejoinable(RestrictInfo *restrictinfo)
Oid opno,
leftOp,
rightOp;
Oid opfamily;
if (restrictinfo->pseudoconstant)
return;
......@@ -1286,14 +1287,17 @@ check_mergejoinable(RestrictInfo *restrictinfo)
opno = ((OpExpr *) clause)->opno;
if (op_mergejoinable(opno,
&leftOp,
&rightOp) &&
if (op_mergejoinable(opno) &&
!contain_volatile_functions((Node *) clause))
{
restrictinfo->mergejoinoperator = opno;
restrictinfo->left_sortop = leftOp;
restrictinfo->right_sortop = rightOp;
/* XXX for the moment, continue to force use of particular sortops */
if (get_op_mergejoin_info(opno, &leftOp, &rightOp, &opfamily))
{
restrictinfo->mergejoinoperator = opno;
restrictinfo->left_sortop = leftOp;
restrictinfo->right_sortop = rightOp;
restrictinfo->mergeopfamily = opfamily;
}
}
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.22 2006/10/04 00:29:54 momjian Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.23 2006/12/23 00:43:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -340,8 +340,8 @@ build_minmax_path(PlannerInfo *root, RelOptInfo *rel, MinMaxAggInfo *info)
Assert(is_opclause(rinfo->clause));
strategy =
get_op_opclass_strategy(((OpExpr *) rinfo->clause)->opno,
index->classlist[prevcol]);
get_op_opfamily_strategy(((OpExpr *) rinfo->clause)->opno,
index->opfamily[prevcol]);
if (strategy == BTEqualStrategyNumber)
break;
}
......@@ -390,10 +390,10 @@ build_minmax_path(PlannerInfo *root, RelOptInfo *rel, MinMaxAggInfo *info)
* Does an aggregate match an index column?
*
* It matches if its argument is equal to the index column's data and its
* sortop is either the LessThan or GreaterThan member of the column's opclass.
* sortop is either a LessThan or GreaterThan member of the column's opfamily.
*
* We return ForwardScanDirection if match the LessThan member,
* BackwardScanDirection if match the GreaterThan member,
* We return ForwardScanDirection if match a LessThan member,
* BackwardScanDirection if match a GreaterThan member,
* and NoMovementScanDirection if there's no match.
*/
static ScanDirection
......@@ -405,9 +405,9 @@ match_agg_to_index_col(MinMaxAggInfo *info, IndexOptInfo *index, int indexcol)
if (!match_index_to_operand((Node *) info->target, indexcol, index))
return NoMovementScanDirection;
/* Look up the operator in the opclass */
strategy = get_op_opclass_strategy(info->aggsortop,
index->classlist[indexcol]);
/* Look up the operator in the opfamily */
strategy = get_op_opfamily_strategy(info->aggsortop,
index->opfamily[indexcol]);
if (strategy == BTLessStrategyNumber)
return ForwardScanDirection;
if (strategy == BTGreaterStrategyNumber)
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.114 2006/12/10 22:13:26 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.115 2006/12/23 00:43:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -689,11 +689,11 @@ convert_IN_to_join(PlannerInfo *root, SubLink *sublink)
return NULL;
if (sublink->testexpr && IsA(sublink->testexpr, OpExpr))
{
List *opclasses;
List *opfamilies;
List *opstrats;
get_op_btree_interpretation(((OpExpr *) sublink->testexpr)->opno,
&opclasses, &opstrats);
&opfamilies, &opstrats);
if (!list_member_int(opstrats, ROWCOMPARE_EQ))
return NULL;
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.224 2006/12/21 16:05:13 petere Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.225 2006/12/23 00:43:10 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
......@@ -1294,13 +1294,9 @@ CommuteRowCompareExpr(RowCompareExpr *clause)
clause->opnos = newops;
/*
* Note: we don't bother to update the opclasses list, but just set it to
* empty. This is OK since this routine is currently only used for index
* quals, and the index machinery won't use the opclass information. The
* original opclass list is NOT valid if we have commuted any cross-type
* comparisons, so don't leave it in place.
* Note: we need not change the opfamilies list; we assume any btree
* opfamily containing an operator will also contain its commutator.
*/
clause->opclasses = NIL; /* XXX */
temp = clause->largs;
clause->largs = clause->rargs;
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.133 2006/10/04 00:29:55 momjian Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.134 2006/12/23 00:43:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1152,6 +1152,10 @@ create_nestloop_path(PlannerInfo *root,
* 'pathkeys' are the path keys of the new join path
* 'mergeclauses' are the RestrictInfo nodes to use as merge clauses
* (this should be a subset of the restrict_clauses list)
* 'mergefamilies' are the btree opfamily OIDs identifying the merge
* ordering for each merge clause
* 'mergestrategies' are the btree operator strategies identifying the merge
* ordering for each merge clause
* 'outersortkeys' are the sort varkeys for the outer relation
* 'innersortkeys' are the sort varkeys for the inner relation
*/
......@@ -1164,6 +1168,8 @@ create_mergejoin_path(PlannerInfo *root,
List *restrict_clauses,
List *pathkeys,
List *mergeclauses,
List *mergefamilies,
List *mergestrategies,
List *outersortkeys,
List *innersortkeys)
{
......@@ -1204,6 +1210,8 @@ create_mergejoin_path(PlannerInfo *root,
pathnode->jpath.joinrestrictinfo = restrict_clauses;
pathnode->jpath.path.pathkeys = pathkeys;
pathnode->path_mergeclauses = mergeclauses;
pathnode->path_mergefamilies = mergefamilies;
pathnode->path_mergestrategies = mergestrategies;
pathnode->outersortkeys = outersortkeys;
pathnode->innersortkeys = innersortkeys;
......
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.128 2006/12/18 18:56:28 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.129 2006/12/23 00:43:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -169,16 +169,16 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info->ncolumns = ncolumns = index->indnatts;
/*
* Need to make classlist and ordering arrays large enough to put
* Need to make opfamily and ordering arrays large enough to put
* a terminating 0 at the end of each one.
*/
info->indexkeys = (int *) palloc(sizeof(int) * ncolumns);
info->classlist = (Oid *) palloc0(sizeof(Oid) * (ncolumns + 1));
info->opfamily = (Oid *) palloc0(sizeof(Oid) * (ncolumns + 1));
info->ordering = (Oid *) palloc0(sizeof(Oid) * (ncolumns + 1));
for (i = 0; i < ncolumns; i++)
{
info->classlist[i] = indexRelation->rd_indclass->values[i];
info->opfamily[i] = indexRelation->rd_opfamily[i];
info->indexkeys[i] = index->indkey.values[i];
}
......
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/predtest.c,v 1.10 2006/10/04 00:29:55 momjian Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/predtest.c,v 1.11 2006/12/23 00:43:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -939,7 +939,7 @@ arrayexpr_cleanup_fn(PredIterInfo info)
* already known immutable, so the clause will certainly always fail.)
*
* Finally, we may be able to deduce something using knowledge about btree
* operator classes; this is encapsulated in btree_predicate_proof().
* operator families; this is encapsulated in btree_predicate_proof().
*----------
*/
static bool
......@@ -989,7 +989,7 @@ predicate_implied_by_simple_clause(Expr *predicate, Node *clause)
* that has "foo" as an input. See notes for implication case.
*
* Finally, we may be able to deduce something using knowledge about btree
* operator classes; this is encapsulated in btree_predicate_proof().
* operator families; this is encapsulated in btree_predicate_proof().
*----------
*/
static bool
......@@ -1062,8 +1062,8 @@ extract_not_arg(Node *clause)
* The strategy numbers defined by btree indexes (see access/skey.h) are:
* (1) < (2) <= (3) = (4) >= (5) >
* and in addition we use (6) to represent <>. <> is not a btree-indexable
* operator, but we assume here that if the equality operator of a btree
* opclass has a negator operator, the negator behaves as <> for the opclass.
* operator, but we assume here that if an equality operator of a btree
* opfamily has a negator operator, the negator behaves as <> for the opfamily.
*
* The interpretation of:
*
......@@ -1146,10 +1146,10 @@ static const StrategyNumber BT_refute_table[6][6] = {
* What we look for here is binary boolean opclauses of the form
* "foo op constant", where "foo" is the same in both clauses. The operators
* and constants can be different but the operators must be in the same btree
* operator class. We use the above operator implication tables to
* operator family. We use the above operator implication tables to
* derive implications between nonidentical clauses. (Note: "foo" is known
* immutable, and constants are surely immutable, but we have to check that
* the operators are too. As of 8.0 it's possible for opclasses to contain
* the operators are too. As of 8.0 it's possible for opfamilies to contain
* operators that are merely stable, and we dare not make deductions with
* these.)
*----------
......@@ -1171,12 +1171,12 @@ btree_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
pred_op_negator,
clause_op_negator,
test_op = InvalidOid;
Oid opclass_id;
Oid opfamily_id;
bool found = false;
StrategyNumber pred_strategy,
clause_strategy,
test_strategy;
Oid clause_subtype;
Oid clause_righttype;
Expr *test_expr;
ExprState *test_exprstate;
Datum test_result;
......@@ -1272,28 +1272,30 @@ btree_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
}
/*
* Try to find a btree opclass containing the needed operators.
* Try to find a btree opfamily containing the needed operators.
*
* We must find a btree opclass that contains both operators, else the
* XXX this needs work!!!!!!!!!!!!!!!!!!!!!!!
*
* We must find a btree opfamily that contains both operators, else the
* implication can't be determined. Also, the pred_op has to be of
* default subtype (implying left and right input datatypes are the same);
* otherwise it's unsafe to put the pred_const on the left side of the
* test. Also, the opclass must contain a suitable test operator matching
* test. Also, the opfamily must contain a suitable test operator matching
* the clause_const's type (which we take to mean that it has the same
* subtype as the original clause_operator).
*
* If there are multiple matching opclasses, assume we can use any one to
* If there are multiple matching opfamilies, assume we can use any one to
* determine the logical relationship of the two operators and the correct
* corresponding test operator. This should work for any logically
* consistent opclasses.
* consistent opfamilies.
*/
catlist = SearchSysCacheList(AMOPOPID, 1,
ObjectIdGetDatum(pred_op),
0, 0, 0);
/*
* If we couldn't find any opclass containing the pred_op, perhaps it is a
* <> operator. See if it has a negator that is in an opclass.
* If we couldn't find any opfamily containing the pred_op, perhaps it is a
* <> operator. See if it has a negator that is in an opfamily.
*/
pred_op_negated = false;
if (catlist->n_members == 0)
......@@ -1312,23 +1314,22 @@ btree_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
/* Also may need the clause_op's negator */
clause_op_negator = get_negator(clause_op);
/* Now search the opclasses */
/* Now search the opfamilies */
for (i = 0; i < catlist->n_members; i++)
{
HeapTuple pred_tuple = &catlist->members[i]->tuple;
Form_pg_amop pred_form = (Form_pg_amop) GETSTRUCT(pred_tuple);
HeapTuple clause_tuple;
opclass_id = pred_form->amopclaid;
/* must be btree */
if (!opclass_is_btree(opclass_id))
if (pred_form->amopmethod != BTREE_AM_OID)
continue;
/* predicate operator must be default within this opclass */
if (pred_form->amopsubtype != InvalidOid)
/* predicate operator must be default within this opfamily */
if (pred_form->amoplefttype != pred_form->amoprighttype)
continue;
/* Get the predicate operator's btree strategy number */
opfamily_id = pred_form->amopfamily;
pred_strategy = (StrategyNumber) pred_form->amopstrategy;
Assert(pred_strategy >= 1 && pred_strategy <= 5);
......@@ -1341,37 +1342,39 @@ btree_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
}
/*
* From the same opclass, find a strategy number for the clause_op, if
* possible
* From the same opfamily, find a strategy number for the clause_op,
* if possible
*/
clause_tuple = SearchSysCache(AMOPOPID,
ObjectIdGetDatum(clause_op),
ObjectIdGetDatum(opclass_id),
ObjectIdGetDatum(opfamily_id),
0, 0);
if (HeapTupleIsValid(clause_tuple))
{
Form_pg_amop clause_form = (Form_pg_amop) GETSTRUCT(clause_tuple);
/* Get the restriction clause operator's strategy/subtype */
/* Get the restriction clause operator's strategy/datatype */
clause_strategy = (StrategyNumber) clause_form->amopstrategy;
Assert(clause_strategy >= 1 && clause_strategy <= 5);
clause_subtype = clause_form->amopsubtype;
Assert(clause_form->amoplefttype == pred_form->amoplefttype);
clause_righttype = clause_form->amoprighttype;
ReleaseSysCache(clause_tuple);
}
else if (OidIsValid(clause_op_negator))
{
clause_tuple = SearchSysCache(AMOPOPID,
ObjectIdGetDatum(clause_op_negator),
ObjectIdGetDatum(opclass_id),
ObjectIdGetDatum(opfamily_id),
0, 0);
if (HeapTupleIsValid(clause_tuple))
{
Form_pg_amop clause_form = (Form_pg_amop) GETSTRUCT(clause_tuple);
/* Get the restriction clause operator's strategy/subtype */
/* Get the restriction clause operator's strategy/datatype */
clause_strategy = (StrategyNumber) clause_form->amopstrategy;
Assert(clause_strategy >= 1 && clause_strategy <= 5);
clause_subtype = clause_form->amopsubtype;
Assert(clause_form->amoplefttype == pred_form->amoplefttype);
clause_righttype = clause_form->amoprighttype;
ReleaseSysCache(clause_tuple);
/* Only consider negators that are = */
......@@ -1400,20 +1403,24 @@ btree_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
}
/*
* See if opclass has an operator for the test strategy and the clause
* datatype.
* See if opfamily has an operator for the test strategy and the
* datatypes.
*/
if (test_strategy == BTNE)
{
test_op = get_opclass_member(opclass_id, clause_subtype,
BTEqualStrategyNumber);
test_op = get_opfamily_member(opfamily_id,
pred_form->amoprighttype,
clause_righttype,
BTEqualStrategyNumber);
if (OidIsValid(test_op))
test_op = get_negator(test_op);
}
else
{
test_op = get_opclass_member(opclass_id, clause_subtype,
test_strategy);
test_op = get_opfamily_member(opfamily_id,
pred_form->amoprighttype,
clause_righttype,
test_strategy);
}
if (OidIsValid(test_op))
{
......@@ -1423,7 +1430,7 @@ btree_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
* Note that we require only the test_op to be immutable, not the
* original clause_op. (pred_op is assumed to have been checked
* immutable by the caller.) Essentially we are assuming that the
* opclass is consistent even if it contains operators that are
* opfamily is consistent even if it contains operators that are
* merely stable.
*/
if (op_volatile(test_op) == PROVOLATILE_IMMUTABLE)
......@@ -1438,7 +1445,7 @@ btree_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
if (!found)
{
/* couldn't find a btree opclass to interpret the operators */
/* couldn't find a btree opfamily to interpret the operators */
return false;
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.49 2006/10/04 00:29:55 momjian Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.50 2006/12/23 00:43:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -342,6 +342,7 @@ make_restrictinfo_internal(Expr *clause,
restrictinfo->mergejoinoperator = InvalidOid;
restrictinfo->left_sortop = InvalidOid;
restrictinfo->right_sortop = InvalidOid;
restrictinfo->mergeopfamily = InvalidOid;
restrictinfo->left_pathkey = NIL;
restrictinfo->right_pathkey = NIL;
......
This diff is collapsed.
......@@ -2,7 +2,7 @@
* ruleutils.c - Functions to convert stored expressions/querytrees
* back to source text
*
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.236 2006/12/21 16:05:15 petere Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.237 2006/12/23 00:43:11 tgl Exp $
**********************************************************************/
#include "postgres.h"
......@@ -19,6 +19,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_trigger.h"
#include "commands/defrem.h"
#include "executor/spi.h"
#include "funcapi.h"
#include "nodes/makefuncs.h"
......@@ -4717,11 +4718,6 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
Form_pg_opclass opcrec;
char *opcname;
char *nspname;
bool isvisible;
/* Domains use their base type's default opclass */
if (OidIsValid(actual_datatype))
actual_datatype = getBaseType(actual_datatype);
ht_opc = SearchSysCache(CLAOID,
ObjectIdGetDatum(opclass),
......@@ -4730,25 +4726,12 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
elog(ERROR, "cache lookup failed for opclass %u", opclass);
opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
/*
* Special case for ARRAY_OPS: pretend it is default for any array type
*/
if (OidIsValid(actual_datatype))
{
if (opcrec->opcintype == ANYARRAYOID &&
OidIsValid(get_element_type(actual_datatype)))
actual_datatype = opcrec->opcintype;
}
/* Must force use of opclass name if not in search path */
isvisible = OpclassIsVisible(opclass);
if (actual_datatype != opcrec->opcintype || !opcrec->opcdefault ||
!isvisible)
if (!OidIsValid(actual_datatype) ||
GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
{
/* Okay, we need the opclass name. Do we need to qualify it? */
opcname = NameStr(opcrec->opcname);
if (isvisible)
if (OpclassIsVisible(opclass))
appendStringInfo(buf, " %s", quote_identifier(opcname));
else
{
......
This diff is collapsed.
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.134 2006/10/06 18:23:35 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/catcache.c,v 1.135 2006/12/23 00:43:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1033,9 +1033,10 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
if (cache->id == INDEXRELID)
{
/*
* Since the OIDs of indexes aren't hardwired, it's painful to figure
* out which is which. Just force all pg_index searches to be heap
* scans while building the relcaches.
* Rather than tracking exactly which indexes have to be loaded
* before we can use indexscans (which changes from time to time),
* just force all pg_index searches to be heap scans until we've
* built the critical relcaches.
*/
if (!criticalRelcachesBuilt)
return false;
......@@ -1051,17 +1052,6 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey)
*/
return false;
}
else if (cache->id == OPEROID)
{
if (!criticalRelcachesBuilt)
{
/* Looking for an OID comparison function? */
Oid lookup_oid = DatumGetObjectId(cur_skey[0].sk_argument);
if (lookup_oid >= MIN_OIDCMP && lookup_oid <= MAX_OIDCMP)
return false;
}
}
/* Normal case, allow index scan */
return true;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -91,7 +91,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/sort/tuplesort.c,v 1.70 2006/10/04 00:30:04 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/sort/tuplesort.c,v 1.71 2006/12/23 00:43:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -2104,15 +2104,16 @@ SelectSortFunction(Oid sortOperator,
int i;
HeapTuple tuple;
Form_pg_operator optup;
Oid opclass = InvalidOid;
Oid opfamily = InvalidOid;
Oid opinputtype = InvalidOid;
/*
* Search pg_amop to see if the target operator is registered as the "<"
* or ">" operator of any btree opclass. It's possible that it might be
* Search pg_amop to see if the target operator is registered as a "<"
* or ">" operator of any btree opfamily. It's possible that it might be
* registered both ways (eg, if someone were to build a "reverse sort"
* opclass for some reason); prefer the "<" case if so. If the operator is
* registered the same way in multiple opclasses, assume we can use the
* associated comparator function from any one.
* opfamily); prefer the "<" case if so. If the operator is registered the
* same way in multiple opfamilies, assume we can use the associated
* comparator function from any one.
*/
catlist = SearchSysCacheList(AMOPOPID, 1,
ObjectIdGetDatum(sortOperator),
......@@ -2125,21 +2126,24 @@ SelectSortFunction(Oid sortOperator,
tuple = &catlist->members[i]->tuple;
aform = (Form_pg_amop) GETSTRUCT(tuple);
if (!opclass_is_btree(aform->amopclaid))
/* must be btree */
if (aform->amopmethod != BTREE_AM_OID)
continue;
/* must be of default subtype, too */
if (aform->amopsubtype != InvalidOid)
/* mustn't be cross-datatype, either */
if (aform->amoplefttype != aform->amoprighttype)
continue;
if (aform->amopstrategy == BTLessStrategyNumber)
{
opclass = aform->amopclaid;
opfamily = aform->amopfamily;
opinputtype = aform->amoplefttype;
*kind = SORTFUNC_CMP;
break; /* done looking */
}
else if (aform->amopstrategy == BTGreaterStrategyNumber)
{
opclass = aform->amopclaid;
opfamily = aform->amopfamily;
opinputtype = aform->amoplefttype;
*kind = SORTFUNC_REVCMP;
/* keep scanning in hopes of finding a BTLess entry */
}
......@@ -2147,10 +2151,13 @@ SelectSortFunction(Oid sortOperator,
ReleaseSysCacheList(catlist);
if (OidIsValid(opclass))
if (OidIsValid(opfamily))
{
/* Found a suitable opclass, get its default comparator function */
*sortFunction = get_opclass_proc(opclass, InvalidOid, BTORDER_PROC);
/* Found a suitable opfamily, get the matching comparator function */
*sortFunction = get_opfamily_proc(opfamily,
opinputtype,
opinputtype,
BTORDER_PROC);
Assert(RegProcedureIsValid(*sortFunction));
return;
}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment