1
0
mirror of https://github.com/postgres/postgres.git synced 2025-12-01 12:18:01 +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

@@ -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
{

View File

@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.215 2006/12/15 18:42:26 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.216 2006/12/23 00:43:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -76,7 +76,7 @@
#include <ctype.h>
#include <math.h>
#include "catalog/pg_opclass.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_type.h"
#include "mb/pg_wchar.h"
@@ -128,7 +128,7 @@ static double convert_timevalue_to_scalar(Datum value, Oid typid);
static bool get_variable_maximum(PlannerInfo *root, VariableStatData *vardata,
Oid sortop, Datum *max);
static Selectivity prefix_selectivity(VariableStatData *vardata,
Oid opclass, Const *prefixcon);
Oid vartype, Oid opfamily, Const *prefixcon);
static Selectivity pattern_selectivity(Const *patt, Pattern_Type ptype);
static Datum string_to_datum(const char *str, Oid datatype);
static Const *string_to_const(const char *str, Oid datatype);
@@ -911,7 +911,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype)
Datum constval;
Oid consttype;
Oid vartype;
Oid opclass;
Oid opfamily;
Pattern_Prefix_Status pstatus;
Const *patt = NULL;
Const *prefix = NULL;
@@ -960,9 +960,9 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype)
* Similarly, the exposed type of the left-hand side should be one of
* those we know. (Do not look at vardata.atttype, which might be
* something binary-compatible but different.) We can use it to choose
* the index opclass from which we must draw the comparison operators.
* the index opfamily from which we must draw the comparison operators.
*
* NOTE: It would be more correct to use the PATTERN opclasses than the
* NOTE: It would be more correct to use the PATTERN opfamilies than the
* simple ones, but at the moment ANALYZE will not generate statistics for
* the PATTERN operators. But our results are so approximate anyway that
* it probably hardly matters.
@@ -972,19 +972,16 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype)
switch (vartype)
{
case TEXTOID:
opclass = TEXT_BTREE_OPS_OID;
break;
case VARCHAROID:
opclass = VARCHAR_BTREE_OPS_OID;
opfamily = TEXT_BTREE_FAM_OID;
break;
case BPCHAROID:
opclass = BPCHAR_BTREE_OPS_OID;
opfamily = BPCHAR_BTREE_FAM_OID;
break;
case NAMEOID:
opclass = NAME_BTREE_OPS_OID;
opfamily = NAME_BTREE_FAM_OID;
break;
case BYTEAOID:
opclass = BYTEA_BTREE_OPS_OID;
opfamily = BYTEA_BTREE_FAM_OID;
break;
default:
ReleaseVariableStats(vardata);
@@ -1028,12 +1025,12 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype)
/*
* Pattern specifies an exact match, so pretend operator is '='
*/
Oid eqopr = get_opclass_member(opclass, InvalidOid,
BTEqualStrategyNumber);
Oid eqopr = get_opfamily_member(opfamily, vartype, vartype,
BTEqualStrategyNumber);
List *eqargs;
if (eqopr == InvalidOid)
elog(ERROR, "no = operator for opclass %u", opclass);
elog(ERROR, "no = operator for opfamily %u", opfamily);
eqargs = list_make2(variable, prefix);
result = DatumGetFloat8(DirectFunctionCall4(eqsel,
PointerGetDatum(root),
@@ -1074,7 +1071,8 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype)
Selectivity restsel;
if (pstatus == Pattern_Prefix_Partial)
prefixsel = prefix_selectivity(&vardata, opclass, prefix);
prefixsel = prefix_selectivity(&vardata, vartype,
opfamily, prefix);
else
prefixsel = 1.0;
restsel = pattern_selectivity(rest, ptype);
@@ -2114,7 +2112,8 @@ icnlikejoinsel(PG_FUNCTION_ARGS)
* we can estimate how much of the input will actually be read. This
* can have a considerable impact on the cost when using indexscans.
*
* clause should be a clause already known to be mergejoinable.
* clause should be a clause already known to be mergejoinable. opfamily and
* strategy specify the sort ordering being used.
*
* *leftscan is set to the fraction of the left-hand variable expected
* to be scanned (0 to 1), and similarly *rightscan for the right-hand
@@ -2122,6 +2121,7 @@ icnlikejoinsel(PG_FUNCTION_ARGS)
*/
void
mergejoinscansel(PlannerInfo *root, Node *clause,
Oid opfamily, int strategy,
Selectivity *leftscan,
Selectivity *rightscan)
{
@@ -2129,15 +2129,14 @@ mergejoinscansel(PlannerInfo *root, Node *clause,
*right;
VariableStatData leftvar,
rightvar;
Oid lefttype,
righttype;
int op_strategy;
Oid op_lefttype;
Oid op_righttype;
bool op_recheck;
Oid opno,
lsortop,
rsortop,
ltop,
gtop,
leop,
revgtop,
revleop;
Datum leftmax,
rightmax;
@@ -2159,15 +2158,51 @@ mergejoinscansel(PlannerInfo *root, Node *clause,
examine_variable(root, left, 0, &leftvar);
examine_variable(root, right, 0, &rightvar);
/* Get the direct input types of the operator */
lefttype = exprType(left);
righttype = exprType(right);
/* Extract the operator's declared left/right datatypes */
get_op_opfamily_properties(opno, opfamily,
&op_strategy,
&op_lefttype,
&op_righttype,
&op_recheck);
Assert(op_strategy == BTEqualStrategyNumber);
Assert(!op_recheck);
/* Verify mergejoinability and get left and right "<" operators */
if (!op_mergejoinable(opno,
&lsortop,
&rsortop))
goto fail; /* shouldn't happen */
/*
* Look up the various operators we need. If we don't find them all,
* it probably means the opfamily is broken, but we cope anyway.
*/
switch (strategy)
{
case BTLessStrategyNumber:
lsortop = get_opfamily_member(opfamily, op_lefttype, op_lefttype,
BTLessStrategyNumber);
rsortop = get_opfamily_member(opfamily, op_righttype, op_righttype,
BTLessStrategyNumber);
leop = get_opfamily_member(opfamily, op_lefttype, op_righttype,
BTLessEqualStrategyNumber);
revleop = get_opfamily_member(opfamily, op_righttype, op_lefttype,
BTLessEqualStrategyNumber);
break;
case BTGreaterStrategyNumber:
/* descending-order case */
lsortop = get_opfamily_member(opfamily, op_lefttype, op_lefttype,
BTGreaterStrategyNumber);
rsortop = get_opfamily_member(opfamily, op_righttype, op_righttype,
BTGreaterStrategyNumber);
leop = get_opfamily_member(opfamily, op_lefttype, op_righttype,
BTGreaterEqualStrategyNumber);
revleop = get_opfamily_member(opfamily, op_righttype, op_lefttype,
BTGreaterEqualStrategyNumber);
break;
default:
goto fail; /* shouldn't get here */
}
if (!OidIsValid(lsortop) ||
!OidIsValid(rsortop) ||
!OidIsValid(leop) ||
!OidIsValid(revleop))
goto fail; /* insufficient info in catalogs */
/* Try to get maximum values of both inputs */
if (!get_variable_maximum(root, &leftvar, lsortop, &leftmax))
@@ -2176,37 +2211,19 @@ mergejoinscansel(PlannerInfo *root, Node *clause,
if (!get_variable_maximum(root, &rightvar, rsortop, &rightmax))
goto fail; /* no max available from stats */
/* Look up the "left < right" and "left > right" operators */
op_mergejoin_crossops(opno, &ltop, &gtop, NULL, NULL);
/* Look up the "left <= right" operator */
leop = get_negator(gtop);
if (!OidIsValid(leop))
goto fail; /* insufficient info in catalogs */
/* Look up the "right > left" operator */
revgtop = get_commutator(ltop);
if (!OidIsValid(revgtop))
goto fail; /* insufficient info in catalogs */
/* Look up the "right <= left" operator */
revleop = get_negator(revgtop);
if (!OidIsValid(revleop))
goto fail; /* insufficient info in catalogs */
/*
* Now, the fraction of the left variable that will be scanned is the
* fraction that's <= the right-side maximum value. But only believe
* non-default estimates, else stick with our 1.0.
*/
selec = scalarineqsel(root, leop, false, &leftvar,
rightmax, righttype);
rightmax, op_righttype);
if (selec != DEFAULT_INEQ_SEL)
*leftscan = selec;
/* And similarly for the right variable. */
selec = scalarineqsel(root, revleop, false, &rightvar,
leftmax, lefttype);
leftmax, op_lefttype);
if (selec != DEFAULT_INEQ_SEL)
*rightscan = selec;
@@ -3486,7 +3503,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
* statistics.
*
* XXX it's conceivable that there are multiple matches with different
* index opclasses; if so, we need to pick one that matches the
* index opfamilies; if so, we need to pick one that matches the
* operator we are estimating for. FIXME later.
*/
ListCell *ilist;
@@ -4100,7 +4117,7 @@ pattern_fixed_prefix(Const *patt, Pattern_Type ptype,
* population represented by the histogram --- the caller must fold this
* together with info about MCVs and NULLs.
*
* We use the >= and < operators from the specified btree opclass to do the
* We use the >= and < operators from the specified btree opfamily to do the
* estimation. The given variable and Const must be of the associated
* datatype.
*
@@ -4110,17 +4127,18 @@ pattern_fixed_prefix(Const *patt, Pattern_Type ptype,
* more useful to use the upper-bound code than not.
*/
static Selectivity
prefix_selectivity(VariableStatData *vardata, Oid opclass, Const *prefixcon)
prefix_selectivity(VariableStatData *vardata,
Oid vartype, Oid opfamily, Const *prefixcon)
{
Selectivity prefixsel;
Oid cmpopr;
FmgrInfo opproc;
Const *greaterstrcon;
cmpopr = get_opclass_member(opclass, InvalidOid,
BTGreaterEqualStrategyNumber);
cmpopr = get_opfamily_member(opfamily, vartype, vartype,
BTGreaterEqualStrategyNumber);
if (cmpopr == InvalidOid)
elog(ERROR, "no >= operator for opclass %u", opclass);
elog(ERROR, "no >= operator for opfamily %u", opfamily);
fmgr_info(get_opcode(cmpopr), &opproc);
prefixsel = ineq_histogram_selectivity(vardata, &opproc, true,
@@ -4143,10 +4161,10 @@ prefix_selectivity(VariableStatData *vardata, Oid opclass, Const *prefixcon)
{
Selectivity topsel;
cmpopr = get_opclass_member(opclass, InvalidOid,
BTLessStrategyNumber);
cmpopr = get_opfamily_member(opfamily, vartype, vartype,
BTLessStrategyNumber);
if (cmpopr == InvalidOid)
elog(ERROR, "no < operator for opclass %u", opclass);
elog(ERROR, "no < operator for opfamily %u", opfamily);
fmgr_info(get_opcode(cmpopr), &opproc);
topsel = ineq_histogram_selectivity(vardata, &opproc, false,
@@ -4921,7 +4939,7 @@ btcostestimate(PG_FUNCTION_ARGS)
}
else if (match_index_to_operand(rightop, indexcol, index))
{
/* Must flip operator to get the opclass member */
/* Must flip operator to get the opfamily member */
clause_op = get_commutator(clause_op);
}
else
@@ -4937,7 +4955,7 @@ btcostestimate(PG_FUNCTION_ARGS)
}
else if (match_index_to_operand(rightop, indexcol, index))
{
/* Must flip operator to get the opclass member */
/* Must flip operator to get the opfamily member */
clause_op = get_commutator(clause_op);
}
else
@@ -4946,9 +4964,9 @@ btcostestimate(PG_FUNCTION_ARGS)
break;
}
}
op_strategy = get_op_opclass_strategy(clause_op,
index->classlist[indexcol]);
Assert(op_strategy != 0); /* not a member of opclass?? */
op_strategy = get_op_opfamily_strategy(clause_op,
index->opfamily[indexcol]);
Assert(op_strategy != 0); /* not a member of opfamily?? */
if (op_strategy == BTEqualStrategyNumber)
eqQualHere = true;
/* count up number of SA scans induced by indexBoundQuals only */

View File

@@ -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;

View File

@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.138 2006/10/04 00:30:00 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.139 2006/12/23 00:43:11 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@@ -37,27 +37,27 @@
/* ---------- AMOP CACHES ---------- */
/*
* op_in_opclass
* op_in_opfamily
*
* Return t iff operator 'opno' is in operator class 'opclass'.
* Return t iff operator 'opno' is in operator family 'opfamily'.
*/
bool
op_in_opclass(Oid opno, Oid opclass)
op_in_opfamily(Oid opno, Oid opfamily)
{
return SearchSysCacheExists(AMOPOPID,
ObjectIdGetDatum(opno),
ObjectIdGetDatum(opclass),
ObjectIdGetDatum(opfamily),
0, 0);
}
/*
* get_op_opclass_strategy
* get_op_opfamily_strategy
*
* Get the operator's strategy number within the specified opclass,
* or 0 if it's not a member of the opclass.
* Get the operator's strategy number within the specified opfamily,
* or 0 if it's not a member of the opfamily.
*/
int
get_op_opclass_strategy(Oid opno, Oid opclass)
get_op_opfamily_strategy(Oid opno, Oid opfamily)
{
HeapTuple tp;
Form_pg_amop amop_tup;
@@ -65,7 +65,7 @@ get_op_opclass_strategy(Oid opno, Oid opclass)
tp = SearchSysCache(AMOPOPID,
ObjectIdGetDatum(opno),
ObjectIdGetDatum(opclass),
ObjectIdGetDatum(opfamily),
0, 0);
if (!HeapTupleIsValid(tp))
return 0;
@@ -76,54 +76,59 @@ get_op_opclass_strategy(Oid opno, Oid opclass)
}
/*
* get_op_opclass_properties
* get_op_opfamily_properties
*
* Get the operator's strategy number, subtype, and recheck (lossy) flag
* within the specified opclass.
* Get the operator's strategy number, input types, and recheck (lossy)
* flag within the specified opfamily.
*
* Caller should already have verified that opno is a member of opclass,
* Caller should already have verified that opno is a member of opfamily,
* therefore we raise an error if the tuple is not found.
*/
void
get_op_opclass_properties(Oid opno, Oid opclass,
int *strategy, Oid *subtype, bool *recheck)
get_op_opfamily_properties(Oid opno, Oid opfamily,
int *strategy,
Oid *lefttype,
Oid *righttype,
bool *recheck)
{
HeapTuple tp;
Form_pg_amop amop_tup;
tp = SearchSysCache(AMOPOPID,
ObjectIdGetDatum(opno),
ObjectIdGetDatum(opclass),
ObjectIdGetDatum(opfamily),
0, 0);
if (!HeapTupleIsValid(tp))
elog(ERROR, "operator %u is not a member of opclass %u",
opno, opclass);
elog(ERROR, "operator %u is not a member of opfamily %u",
opno, opfamily);
amop_tup = (Form_pg_amop) GETSTRUCT(tp);
*strategy = amop_tup->amopstrategy;
*subtype = amop_tup->amopsubtype;
*lefttype = amop_tup->amoplefttype;
*righttype = amop_tup->amoprighttype;
*recheck = amop_tup->amopreqcheck;
ReleaseSysCache(tp);
}
/*
* get_opclass_member
* get_opfamily_member
* Get the OID of the operator that implements the specified strategy
* with the specified subtype for the specified opclass.
* with the specified datatypes for the specified opfamily.
*
* Returns InvalidOid if there is no pg_amop entry for the given keys.
*/
Oid
get_opclass_member(Oid opclass, Oid subtype, int16 strategy)
get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype,
int16 strategy)
{
HeapTuple tp;
Form_pg_amop amop_tup;
Oid result;
tp = SearchSysCache(AMOPSTRATEGY,
ObjectIdGetDatum(opclass),
ObjectIdGetDatum(subtype),
Int16GetDatum(strategy),
0);
ObjectIdGetDatum(opfamily),
ObjectIdGetDatum(lefttype),
ObjectIdGetDatum(righttype),
Int16GetDatum(strategy));
if (!HeapTupleIsValid(tp))
return InvalidOid;
amop_tup = (Form_pg_amop) GETSTRUCT(tp);
@@ -132,11 +137,161 @@ get_opclass_member(Oid opclass, Oid subtype, int16 strategy)
return result;
}
/*
* get_op_mergejoin_info
* Given the OIDs of a (putatively) mergejoinable equality operator
* and a sortop defining the sort ordering of the lefthand input of
* the merge clause, determine whether this sort ordering is actually
* usable for merging. If so, return the required sort ordering op
* for the righthand input, as well as the btree opfamily OID containing
* these operators and the operator strategy number of the two sortops
* (either BTLessStrategyNumber or BTGreaterStrategyNumber).
*
* We can mergejoin if we find the two operators in the same opfamily as
* equality and either less-than or greater-than respectively. If there
* are multiple such opfamilies, assume we can use any one.
*/
#ifdef NOT_YET
/* eventually should look like this */
bool
get_op_mergejoin_info(Oid eq_op, Oid left_sortop,
Oid *right_sortop, Oid *opfamily, int *opstrategy)
{
bool result = false;
Oid lefttype;
Oid righttype;
CatCList *catlist;
int i;
/* Make sure output args are initialized even on failure */
*right_sortop = InvalidOid;
*opfamily = InvalidOid;
*opstrategy = 0;
/* Need the righthand input datatype */
op_input_types(eq_op, &lefttype, &righttype);
/*
* Search through all the pg_amop entries containing the equality operator
*/
catlist = SearchSysCacheList(AMOPOPID, 1,
ObjectIdGetDatum(eq_op),
0, 0, 0);
for (i = 0; i < catlist->n_members; i++)
{
HeapTuple op_tuple = &catlist->members[i]->tuple;
Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
Oid opfamily_id;
StrategyNumber op_strategy;
/* must be btree */
if (op_form->amopmethod != BTREE_AM_OID)
continue;
/* must use the operator as equality */
if (op_form->amopstrategy != BTEqualStrategyNumber)
continue;
/* See if sort operator is also in this opclass with OK semantics */
opfamily_id = op_form->amopfamily;
op_strategy = get_op_opfamily_strategy(left_sortop, opfamily_id);
if (op_strategy == BTLessStrategyNumber ||
op_strategy == BTGreaterStrategyNumber)
{
/* Yes, so find the corresponding righthand sortop */
*right_sortop = get_opfamily_member(opfamily_id,
righttype,
righttype,
op_strategy);
if (OidIsValid(*right_sortop))
{
/* Found a workable mergejoin semantics */
*opfamily = opfamily_id;
*opstrategy = op_strategy;
result = true;
break;
}
}
}
ReleaseSysCacheList(catlist);
return result;
}
#else
/* temp implementation until planner gets smarter: left_sortop is output */
bool
get_op_mergejoin_info(Oid eq_op, Oid *left_sortop,
Oid *right_sortop, Oid *opfamily)
{
bool result = false;
Oid lefttype;
Oid righttype;
CatCList *catlist;
int i;
/* Make sure output args are initialized even on failure */
*left_sortop = InvalidOid;
*right_sortop = InvalidOid;
*opfamily = InvalidOid;
/* Need the input datatypes */
op_input_types(eq_op, &lefttype, &righttype);
/*
* Search through all the pg_amop entries containing the equality operator
*/
catlist = SearchSysCacheList(AMOPOPID, 1,
ObjectIdGetDatum(eq_op),
0, 0, 0);
for (i = 0; i < catlist->n_members; i++)
{
HeapTuple op_tuple = &catlist->members[i]->tuple;
Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
Oid opfamily_id;
/* must be btree */
if (op_form->amopmethod != BTREE_AM_OID)
continue;
/* must use the operator as equality */
if (op_form->amopstrategy != BTEqualStrategyNumber)
continue;
opfamily_id = op_form->amopfamily;
/* Find the matching sortops */
*left_sortop = get_opfamily_member(opfamily_id,
lefttype,
lefttype,
BTLessStrategyNumber);
*right_sortop = get_opfamily_member(opfamily_id,
righttype,
righttype,
BTLessStrategyNumber);
if (OidIsValid(*left_sortop) && OidIsValid(*right_sortop))
{
/* Found a workable mergejoin semantics */
*opfamily = opfamily_id;
result = true;
break;
}
}
ReleaseSysCacheList(catlist);
return result;
}
#endif
/*
* get_op_hash_function
* Get the OID of the datatype-specific hash function associated with
* a hashable equality operator.
*
* XXX API needs to be generalized for the case of different left and right
* datatypes.
*
* Returns InvalidOid if no hash function can be found. (This indicates
* that the operator should not have been marked oprcanhash.)
*/
@@ -145,12 +300,12 @@ get_op_hash_function(Oid opno)
{
CatCList *catlist;
int i;
Oid opclass = InvalidOid;
Oid result = InvalidOid;
/*
* Search pg_amop to see if the target operator is registered as the "="
* operator of any hash opclass. If the operator is registered in
* multiple opclasses, assume we can use the associated hash function from
* operator of any hash opfamily. If the operator is registered in
* multiple opfamilies, assume we can use the associated hash function from
* any one.
*/
catlist = SearchSysCacheList(AMOPOPID, 1,
@@ -162,57 +317,43 @@ get_op_hash_function(Oid opno)
HeapTuple tuple = &catlist->members[i]->tuple;
Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
if (aform->amopstrategy == HTEqualStrategyNumber &&
opclass_is_hash(aform->amopclaid))
if (aform->amopmethod == HASH_AM_OID &&
aform->amopstrategy == HTEqualStrategyNumber)
{
opclass = aform->amopclaid;
/* Found a suitable opfamily, get matching hash support function */
result = get_opfamily_proc(aform->amopfamily,
aform->amoplefttype,
aform->amoprighttype,
HASHPROC);
break;
}
}
ReleaseSysCacheList(catlist);
if (OidIsValid(opclass))
{
/* Found a suitable opclass, get its default hash support function */
return get_opclass_proc(opclass, InvalidOid, HASHPROC);
}
/* Didn't find a match... */
return InvalidOid;
return result;
}
/*
* get_op_btree_interpretation
* Given an operator's OID, find out which btree opclasses it belongs to,
* Given an operator's OID, find out which btree opfamilies it belongs to,
* and what strategy number it has within each one. The results are
* returned as an OID list and a parallel integer list.
*
* In addition to the normal btree operators, we consider a <> operator to be
* a "member" of an opclass if its negator is the opclass' equality operator.
* ROWCOMPARE_NE is returned as the strategy number for this case.
* a "member" of an opfamily if its negator is an equality operator of the
* opfamily. ROWCOMPARE_NE is returned as the strategy number for this case.
*/
void
get_op_btree_interpretation(Oid opno, List **opclasses, List **opstrats)
get_op_btree_interpretation(Oid opno, List **opfamilies, List **opstrats)
{
Oid lefttype,
righttype;
CatCList *catlist;
bool op_negated;
int i;
*opclasses = NIL;
*opfamilies = NIL;
*opstrats = NIL;
/*
* Get the nominal left-hand input type of the operator; we will ignore
* opclasses that don't have that as the expected input datatype. This is
* a kluge to avoid being confused by binary-compatible opclasses (such as
* text_ops and varchar_ops, which share the same operators).
*/
op_input_types(opno, &lefttype, &righttype);
Assert(OidIsValid(lefttype));
/*
* Find all the pg_amop entries containing the operator.
*/
@@ -221,8 +362,8 @@ get_op_btree_interpretation(Oid opno, List **opclasses, List **opstrats)
0, 0, 0);
/*
* If we can't find any opclass containing the op, perhaps it is a <>
* operator. See if it has a negator that is in an opclass.
* If we can't find any opfamily containing the op, perhaps it is a <>
* operator. See if it has a negator that is in an opfamily.
*/
op_negated = false;
if (catlist->n_members == 0)
@@ -239,25 +380,20 @@ get_op_btree_interpretation(Oid opno, List **opclasses, List **opstrats)
}
}
/* Now search the opclasses */
/* Now search the opfamilies */
for (i = 0; i < catlist->n_members; i++)
{
HeapTuple op_tuple = &catlist->members[i]->tuple;
Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
Oid opclass_id;
Oid opfamily_id;
StrategyNumber op_strategy;
opclass_id = op_form->amopclaid;
/* must be btree */
if (!opclass_is_btree(opclass_id))
continue;
/* must match operator input type exactly */
if (get_opclass_input_type(opclass_id) != lefttype)
if (op_form->amopmethod != BTREE_AM_OID)
continue;
/* Get the operator's btree strategy number */
opfamily_id = op_form->amopfamily;
op_strategy = (StrategyNumber) op_form->amopstrategy;
Assert(op_strategy >= 1 && op_strategy <= 5);
@@ -269,7 +405,7 @@ get_op_btree_interpretation(Oid opno, List **opclasses, List **opstrats)
op_strategy = ROWCOMPARE_NE;
}
*opclasses = lappend_oid(*opclasses, opclass_id);
*opfamilies = lappend_oid(*opfamilies, opfamily_id);
*opstrats = lappend_int(*opstrats, op_strategy);
}
@@ -280,24 +416,24 @@ get_op_btree_interpretation(Oid opno, List **opclasses, List **opstrats)
/* ---------- AMPROC CACHES ---------- */
/*
* get_opclass_proc
* get_opfamily_proc
* Get the OID of the specified support function
* for the specified opclass and subtype.
* for the specified opfamily and datatypes.
*
* Returns InvalidOid if there is no pg_amproc entry for the given keys.
*/
Oid
get_opclass_proc(Oid opclass, Oid subtype, int16 procnum)
get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
{
HeapTuple tp;
Form_pg_amproc amproc_tup;
RegProcedure result;
tp = SearchSysCache(AMPROCNUM,
ObjectIdGetDatum(opclass),
ObjectIdGetDatum(subtype),
Int16GetDatum(procnum),
0);
ObjectIdGetDatum(opfamily),
ObjectIdGetDatum(lefttype),
ObjectIdGetDatum(righttype),
Int16GetDatum(procnum));
if (!HeapTupleIsValid(tp))
return InvalidOid;
amproc_tup = (Form_pg_amproc) GETSTRUCT(tp);
@@ -477,17 +613,16 @@ get_atttypetypmod(Oid relid, AttrNumber attnum,
/* ---------- OPCLASS CACHE ---------- */
/*
* opclass_is_btree
* get_opclass_family
*
* Returns TRUE iff the specified opclass is associated with the
* btree index access method.
* Returns the OID of the operator family the opclass belongs to.
*/
bool
opclass_is_btree(Oid opclass)
Oid
get_opclass_family(Oid opclass)
{
HeapTuple tp;
Form_pg_opclass cla_tup;
bool result;
Oid result;
tp = SearchSysCache(CLAOID,
ObjectIdGetDatum(opclass),
@@ -496,57 +631,7 @@ opclass_is_btree(Oid opclass)
elog(ERROR, "cache lookup failed for opclass %u", opclass);
cla_tup = (Form_pg_opclass) GETSTRUCT(tp);
result = (cla_tup->opcamid == BTREE_AM_OID);
ReleaseSysCache(tp);
return result;
}
/*
* opclass_is_hash
*
* Returns TRUE iff the specified opclass is associated with the
* hash index access method.
*/
bool
opclass_is_hash(Oid opclass)
{
HeapTuple tp;
Form_pg_opclass cla_tup;
bool result;
tp = SearchSysCache(CLAOID,
ObjectIdGetDatum(opclass),
0, 0, 0);
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for opclass %u", opclass);
cla_tup = (Form_pg_opclass) GETSTRUCT(tp);
result = (cla_tup->opcamid == HASH_AM_OID);
ReleaseSysCache(tp);
return result;
}
/*
* opclass_is_default
*
* Returns TRUE iff the specified opclass is the default for its
* index access method and input data type.
*/
bool
opclass_is_default(Oid opclass)
{
HeapTuple tp;
Form_pg_opclass cla_tup;
bool result;
tp = SearchSysCache(CLAOID,
ObjectIdGetDatum(opclass),
0, 0, 0);
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for opclass %u", opclass);
cla_tup = (Form_pg_opclass) GETSTRUCT(tp);
result = cla_tup->opcdefault;
result = cla_tup->opcfamily;
ReleaseSysCache(tp);
return result;
}
@@ -657,11 +742,13 @@ op_input_types(Oid opno, Oid *lefttype, Oid *righttype)
/*
* op_mergejoinable
*
* Returns the left and right sort operators corresponding to a
* mergejoinable operator, or false if the operator is not mergejoinable.
* Returns true if the operator is potentially mergejoinable. (The planner
* will fail to find any mergejoin plans unless there are suitable btree
* opfamily entries for this operator and associated sortops. The pg_operator
* flag is just a hint to tell the planner whether to bother looking.)
*/
bool
op_mergejoinable(Oid opno, Oid *leftOp, Oid *rightOp)
op_mergejoinable(Oid opno)
{
HeapTuple tp;
bool result = false;
@@ -673,65 +760,17 @@ op_mergejoinable(Oid opno, Oid *leftOp, Oid *rightOp)
{
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
if (optup->oprlsortop &&
optup->oprrsortop)
{
*leftOp = optup->oprlsortop;
*rightOp = optup->oprrsortop;
result = true;
}
result = optup->oprcanmerge;
ReleaseSysCache(tp);
}
return result;
}
/*
* op_mergejoin_crossops
*
* Returns the cross-type comparison operators (ltype "<" rtype and
* ltype ">" rtype) for an operator previously determined to be
* mergejoinable. Optionally, fetches the regproc ids of these
* operators, as well as their operator OIDs.
*/
void
op_mergejoin_crossops(Oid opno, Oid *ltop, Oid *gtop,
RegProcedure *ltproc, RegProcedure *gtproc)
{
HeapTuple tp;
Form_pg_operator optup;
/*
* Get the declared comparison operators of the operator.
*/
tp = SearchSysCache(OPEROID,
ObjectIdGetDatum(opno),
0, 0, 0);
if (!HeapTupleIsValid(tp)) /* shouldn't happen */
elog(ERROR, "cache lookup failed for operator %u", opno);
optup = (Form_pg_operator) GETSTRUCT(tp);
*ltop = optup->oprltcmpop;
*gtop = optup->oprgtcmpop;
ReleaseSysCache(tp);
/* Check < op provided */
if (!OidIsValid(*ltop))
elog(ERROR, "mergejoin operator %u has no matching < operator",
opno);
if (ltproc)
*ltproc = get_opcode(*ltop);
/* Check > op provided */
if (!OidIsValid(*gtop))
elog(ERROR, "mergejoin operator %u has no matching > operator",
opno);
if (gtproc)
*gtproc = get_opcode(*gtop);
}
/*
* op_hashjoinable
*
* Returns true if the operator is hashjoinable.
* Returns true if the operator is hashjoinable. (There must be a suitable
* hash opfamily entry for this operator if it is so marked.)
*/
bool
op_hashjoinable(Oid opno)

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.250 2006/11/05 23:40:30 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.251 2006/12/23 00:43:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -69,7 +69,7 @@
*/
#define RELCACHE_INIT_FILENAME "pg_internal.init"
#define RELCACHE_INIT_FILEMAGIC 0x573263 /* version ID value */
#define RELCACHE_INIT_FILEMAGIC 0x573264 /* version ID value */
/*
* hardcoded tuple descriptors. see include/catalog/pg_attribute.h
@@ -159,7 +159,8 @@ do { \
/*
* Special cache for opclass-related information
*
* Note: only default-subtype operators and support procs get cached
* Note: only default operators and support procs get cached, ie, those with
* lefttype = righttype = opcintype.
*/
typedef struct opclasscacheent
{
@@ -167,6 +168,8 @@ typedef struct opclasscacheent
bool valid; /* set TRUE after successful fill-in */
StrategyNumber numStrats; /* max # of strategies (from pg_am) */
StrategyNumber numSupport; /* max # of support procs (from pg_am) */
Oid opcfamily; /* OID of opclass's family */
Oid opcintype; /* OID of opclass's declared input type */
Oid *operatorOids; /* strategy operators' OIDs */
RegProcedure *supportProcs; /* support procs */
} OpClassCacheEnt;
@@ -201,6 +204,8 @@ static List *insert_ordered_oid(List *list, Oid datum);
static void IndexSupportInitialize(oidvector *indclass,
Oid *indexOperator,
RegProcedure *indexSupport,
Oid *opFamily,
Oid *opcInType,
StrategyNumber maxStrategyNumber,
StrategyNumber maxSupportNumber,
AttrNumber maxAttributeNumber);
@@ -921,11 +926,9 @@ RelationInitIndexAccessInfo(Relation relation)
Form_pg_am aform;
Datum indclassDatum;
bool isnull;
oidvector *indclass;
MemoryContext indexcxt;
MemoryContext oldcontext;
Oid *operator;
RegProcedure *support;
FmgrInfo *supportinfo;
int natts;
uint16 amstrategies;
uint16 amsupport;
@@ -947,18 +950,6 @@ RelationInitIndexAccessInfo(Relation relation)
MemoryContextSwitchTo(oldcontext);
ReleaseSysCache(tuple);
/*
* indclass cannot be referenced directly through the C struct, because it
* is after the variable-width indkey field. Therefore we extract the
* datum the hard way and provide a direct link in the relcache.
*/
indclassDatum = fastgetattr(relation->rd_indextuple,
Anum_pg_index_indclass,
GetPgIndexDescriptor(),
&isnull);
Assert(!isnull);
relation->rd_indclass = (oidvector *) DatumGetPointer(indclassDatum);
/*
* Make a copy of the pg_am entry for the index's access method
*/
@@ -1001,38 +992,53 @@ RelationInitIndexAccessInfo(Relation relation)
relation->rd_aminfo = (RelationAmInfo *)
MemoryContextAllocZero(indexcxt, sizeof(RelationAmInfo));
relation->rd_opfamily = (Oid *)
MemoryContextAllocZero(indexcxt, natts * sizeof(Oid));
relation->rd_opcintype = (Oid *)
MemoryContextAllocZero(indexcxt, natts * sizeof(Oid));
if (amstrategies > 0)
operator = (Oid *)
relation->rd_operator = (Oid *)
MemoryContextAllocZero(indexcxt,
natts * amstrategies * sizeof(Oid));
else
operator = NULL;
relation->rd_operator = NULL;
if (amsupport > 0)
{
int nsupport = natts * amsupport;
support = (RegProcedure *)
relation->rd_support = (RegProcedure *)
MemoryContextAllocZero(indexcxt, nsupport * sizeof(RegProcedure));
supportinfo = (FmgrInfo *)
relation->rd_supportinfo = (FmgrInfo *)
MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
}
else
{
support = NULL;
supportinfo = NULL;
relation->rd_support = NULL;
relation->rd_supportinfo = NULL;
}
relation->rd_operator = operator;
relation->rd_support = support;
relation->rd_supportinfo = supportinfo;
/*
* indclass cannot be referenced directly through the C struct, because it
* comes after the variable-width indkey field. Must extract the
* datum the hard way...
*/
indclassDatum = fastgetattr(relation->rd_indextuple,
Anum_pg_index_indclass,
GetPgIndexDescriptor(),
&isnull);
Assert(!isnull);
indclass = (oidvector *) DatumGetPointer(indclassDatum);
/*
* Fill the operator and support procedure OID arrays. (aminfo and
* Fill the operator and support procedure OID arrays, as well as the
* info about opfamilies and opclass input types. (aminfo and
* supportinfo are left as zeroes, and are filled on-the-fly when used)
*/
IndexSupportInitialize(relation->rd_indclass,
operator, support,
IndexSupportInitialize(indclass,
relation->rd_operator, relation->rd_support,
relation->rd_opfamily, relation->rd_opcintype,
amstrategies, amsupport, natts);
/*
@@ -1048,8 +1054,8 @@ RelationInitIndexAccessInfo(Relation relation)
* Initializes an index's cached opclass information,
* given the index's pg_index.indclass entry.
*
* Data is returned into *indexOperator and *indexSupport, which are arrays
* allocated by the caller.
* Data is returned into *indexOperator, *indexSupport, *opFamily, and
* *opcInType, which are arrays allocated by the caller.
*
* The caller also passes maxStrategyNumber, maxSupportNumber, and
* maxAttributeNumber, since these indicate the size of the arrays
@@ -1061,6 +1067,8 @@ static void
IndexSupportInitialize(oidvector *indclass,
Oid *indexOperator,
RegProcedure *indexSupport,
Oid *opFamily,
Oid *opcInType,
StrategyNumber maxStrategyNumber,
StrategyNumber maxSupportNumber,
AttrNumber maxAttributeNumber)
@@ -1080,6 +1088,8 @@ IndexSupportInitialize(oidvector *indclass,
maxSupportNumber);
/* copy cached data into relcache entry */
opFamily[attIndex] = opcentry->opcfamily;
opcInType[attIndex] = opcentry->opcintype;
if (maxStrategyNumber > 0)
memcpy(&indexOperator[attIndex * maxStrategyNumber],
opcentry->operatorOids,
@@ -1116,7 +1126,7 @@ LookupOpclassInfo(Oid operatorClassOid,
bool found;
Relation rel;
SysScanDesc scan;
ScanKeyData skey[2];
ScanKeyData skey[3];
HeapTuple htup;
bool indexOK;
@@ -1176,23 +1186,55 @@ LookupOpclassInfo(Oid operatorClassOid,
(operatorClassOid != OID_BTREE_OPS_OID &&
operatorClassOid != INT2_BTREE_OPS_OID);
/*
* We have to fetch the pg_opclass row to determine its opfamily and
* opcintype, which are needed to look up the operators and functions.
* It'd be convenient to use the syscache here, but that probably doesn't
* work while bootstrapping.
*/
ScanKeyInit(&skey[0],
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(operatorClassOid));
rel = heap_open(OperatorClassRelationId, AccessShareLock);
scan = systable_beginscan(rel, OpclassOidIndexId, indexOK,
SnapshotNow, 1, skey);
if (HeapTupleIsValid(htup = systable_getnext(scan)))
{
Form_pg_opclass opclassform = (Form_pg_opclass) GETSTRUCT(htup);
opcentry->opcfamily = opclassform->opcfamily;
opcentry->opcintype = opclassform->opcintype;
}
else
elog(ERROR, "could not find tuple for opclass %u", operatorClassOid);
systable_endscan(scan);
heap_close(rel, AccessShareLock);
/*
* Scan pg_amop to obtain operators for the opclass. We only fetch the
* default ones (those with subtype zero).
* default ones (those with lefttype = righttype = opcintype).
*/
if (numStrats > 0)
{
ScanKeyInit(&skey[0],
Anum_pg_amop_amopclaid,
Anum_pg_amop_amopfamily,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(operatorClassOid));
ObjectIdGetDatum(opcentry->opcfamily));
ScanKeyInit(&skey[1],
Anum_pg_amop_amopsubtype,
Anum_pg_amop_amoplefttype,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(InvalidOid));
ObjectIdGetDatum(opcentry->opcintype));
ScanKeyInit(&skey[2],
Anum_pg_amop_amoprighttype,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(opcentry->opcintype));
rel = heap_open(AccessMethodOperatorRelationId, AccessShareLock);
scan = systable_beginscan(rel, AccessMethodStrategyIndexId, indexOK,
SnapshotNow, 2, skey);
SnapshotNow, 3, skey);
while (HeapTupleIsValid(htup = systable_getnext(scan)))
{
@@ -1212,21 +1254,25 @@ LookupOpclassInfo(Oid operatorClassOid,
/*
* Scan pg_amproc to obtain support procs for the opclass. We only fetch
* the default ones (those with subtype zero).
* the default ones (those with lefttype = righttype = opcintype).
*/
if (numSupport > 0)
{
ScanKeyInit(&skey[0],
Anum_pg_amproc_amopclaid,
Anum_pg_amproc_amprocfamily,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(operatorClassOid));
ObjectIdGetDatum(opcentry->opcfamily));
ScanKeyInit(&skey[1],
Anum_pg_amproc_amprocsubtype,
Anum_pg_amproc_amproclefttype,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(InvalidOid));
ObjectIdGetDatum(opcentry->opcintype));
ScanKeyInit(&skey[2],
Anum_pg_amproc_amprocrighttype,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(opcentry->opcintype));
rel = heap_open(AccessMethodProcedureRelationId, AccessShareLock);
scan = systable_beginscan(rel, AccessMethodProcedureIndexId, indexOK,
SnapshotNow, 2, skey);
SnapshotNow, 3, skey);
while (HeapTupleIsValid(htup = systable_getnext(scan)))
{
@@ -3097,8 +3143,6 @@ load_relcache_init_file(void)
Relation rel;
Form_pg_class relform;
bool has_not_null;
Datum indclassDatum;
bool isnull;
/* first read the relation descriptor length */
if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
@@ -3187,6 +3231,8 @@ load_relcache_init_file(void)
{
Form_pg_am am;
MemoryContext indexcxt;
Oid *opfamily;
Oid *opcintype;
Oid *operator;
RegProcedure *support;
int nsupport;
@@ -3207,14 +3253,6 @@ load_relcache_init_file(void)
rel->rd_indextuple->t_data = (HeapTupleHeader) ((char *) rel->rd_indextuple + HEAPTUPLESIZE);
rel->rd_index = (Form_pg_index) GETSTRUCT(rel->rd_indextuple);
/* fix up indclass pointer too */
indclassDatum = fastgetattr(rel->rd_indextuple,
Anum_pg_index_indclass,
GetPgIndexDescriptor(),
&isnull);
Assert(!isnull);
rel->rd_indclass = (oidvector *) DatumGetPointer(indclassDatum);
/* next, read the access method tuple form */
if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
goto read_failed;
@@ -3235,6 +3273,26 @@ load_relcache_init_file(void)
ALLOCSET_SMALL_MAXSIZE);
rel->rd_indexcxt = indexcxt;
/* next, read the vector of opfamily OIDs */
if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
goto read_failed;
opfamily = (Oid *) MemoryContextAlloc(indexcxt, len);
if ((nread = fread(opfamily, 1, len, fp)) != len)
goto read_failed;
rel->rd_opfamily = opfamily;
/* next, read the vector of opcintype OIDs */
if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
goto read_failed;
opcintype = (Oid *) MemoryContextAlloc(indexcxt, len);
if ((nread = fread(opcintype, 1, len, fp)) != len)
goto read_failed;
rel->rd_opcintype = opcintype;
/* next, read the vector of operator OIDs */
if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
goto read_failed;
@@ -3269,10 +3327,11 @@ load_relcache_init_file(void)
Assert(rel->rd_index == NULL);
Assert(rel->rd_indextuple == NULL);
Assert(rel->rd_indclass == NULL);
Assert(rel->rd_am == NULL);
Assert(rel->rd_indexcxt == NULL);
Assert(rel->rd_aminfo == NULL);
Assert(rel->rd_opfamily == NULL);
Assert(rel->rd_opcintype == NULL);
Assert(rel->rd_operator == NULL);
Assert(rel->rd_support == NULL);
Assert(rel->rd_supportinfo == NULL);
@@ -3450,6 +3509,16 @@ write_relcache_init_file(void)
/* next, write the access method tuple form */
write_item(am, sizeof(FormData_pg_am), fp);
/* next, write the vector of opfamily OIDs */
write_item(rel->rd_opfamily,
relform->relnatts * sizeof(Oid),
fp);
/* next, write the vector of opcintype OIDs */
write_item(rel->rd_opcintype,
relform->relnatts * sizeof(Oid),
fp);
/* next, write the vector of operator OIDs */
write_item(rel->rd_operator,
relform->relnatts * (am->amstrategies * sizeof(Oid)),

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.108 2006/10/06 18:23:35 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.109 2006/12/23 00:43:11 tgl Exp $
*
* NOTES
* These routines allow the parser/planner/executor to perform
@@ -30,11 +30,11 @@
#include "catalog/pg_cast.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_language.h"
#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_statistic.h"
@@ -135,7 +135,7 @@ static const struct cachedesc cacheinfo[] = {
2,
{
Anum_pg_amop_amopopr,
Anum_pg_amop_amopclaid,
Anum_pg_amop_amopfamily,
0,
0
},
@@ -144,24 +144,24 @@ static const struct cachedesc cacheinfo[] = {
{AccessMethodOperatorRelationId, /* AMOPSTRATEGY */
AccessMethodStrategyIndexId,
0,
3,
4,
{
Anum_pg_amop_amopclaid,
Anum_pg_amop_amopsubtype,
Anum_pg_amop_amopstrategy,
0
Anum_pg_amop_amopfamily,
Anum_pg_amop_amoplefttype,
Anum_pg_amop_amoprighttype,
Anum_pg_amop_amopstrategy
},
64
},
{AccessMethodProcedureRelationId, /* AMPROCNUM */
AccessMethodProcedureIndexId,
0,
3,
4,
{
Anum_pg_amproc_amopclaid,
Anum_pg_amproc_amprocsubtype,
Anum_pg_amproc_amprocnum,
0
Anum_pg_amproc_amprocfamily,
Anum_pg_amproc_amproclefttype,
Anum_pg_amproc_amprocrighttype,
Anum_pg_amproc_amprocnum
},
64
},
@@ -255,7 +255,7 @@ static const struct cachedesc cacheinfo[] = {
0,
3,
{
Anum_pg_opclass_opcamid,
Anum_pg_opclass_opcmethod,
Anum_pg_opclass_opcname,
Anum_pg_opclass_opcnamespace,
0
@@ -334,18 +334,6 @@ static const struct cachedesc cacheinfo[] = {
},
1024
},
{InheritsRelationId, /* INHRELID */
InheritsRelidSeqnoIndexId,
Anum_pg_inherits_inhrelid,
2,
{
Anum_pg_inherits_inhrelid,
Anum_pg_inherits_inhseqno,
0,
0
},
256
},
{LanguageRelationId, /* LANGNAME */
LanguageNameIndexId,
0,
@@ -418,6 +406,30 @@ static const struct cachedesc cacheinfo[] = {
},
1024
},
{OperatorFamilyRelationId, /* OPFAMILYAMNAMENSP */
OpfamilyAmNameNspIndexId,
0,
3,
{
Anum_pg_opfamily_opfmethod,
Anum_pg_opfamily_opfname,
Anum_pg_opfamily_opfnamespace,
0
},
64
},
{OperatorFamilyRelationId, /* OPFAMILYOID */
OpfamilyOidIndexId,
0,
1,
{
ObjectIdAttributeNumber,
0,
0,
0
},
64
},
{ProcedureRelationId, /* PROCNAMEARGSNSP */
ProcedureNameArgsNspIndexId,
0,

View File

@@ -36,7 +36,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.22 2006/10/04 00:30:01 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.23 2006/12/23 00:43:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -165,17 +165,30 @@ lookup_type_cache(Oid type_id, int flags)
/* If we haven't already found the opclass, try to do so */
if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_LT_OPR | TYPECACHE_GT_OPR |
TYPECACHE_CMP_PROC |
TYPECACHE_EQ_OPR_FINFO | TYPECACHE_CMP_PROC_FINFO)) &&
typentry->btree_opc == InvalidOid)
TYPECACHE_EQ_OPR_FINFO | TYPECACHE_CMP_PROC_FINFO |
TYPECACHE_BTREE_OPFAMILY)) &&
typentry->btree_opf == InvalidOid)
{
typentry->btree_opc = GetDefaultOpClass(type_id,
BTREE_AM_OID);
/* Only care about hash opclass if no btree opclass... */
if (typentry->btree_opc == InvalidOid)
Oid opclass;
opclass = GetDefaultOpClass(type_id, BTREE_AM_OID);
if (OidIsValid(opclass))
{
if (typentry->hash_opc == InvalidOid)
typentry->hash_opc = GetDefaultOpClass(type_id,
HASH_AM_OID);
typentry->btree_opf = get_opclass_family(opclass);
typentry->btree_opintype = get_opclass_input_type(opclass);
}
/* Only care about hash opclass if no btree opclass... */
if (typentry->btree_opf == InvalidOid)
{
if (typentry->hash_opf == InvalidOid)
{
opclass = GetDefaultOpClass(type_id, HASH_AM_OID);
if (OidIsValid(opclass))
{
typentry->hash_opf = get_opclass_family(opclass);
typentry->hash_opintype = get_opclass_input_type(opclass);
}
}
}
else
{
@@ -193,37 +206,42 @@ lookup_type_cache(Oid type_id, int flags)
if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
typentry->eq_opr == InvalidOid)
{
if (typentry->btree_opc != InvalidOid)
typentry->eq_opr = get_opclass_member(typentry->btree_opc,
InvalidOid,
BTEqualStrategyNumber);
if (typentry->btree_opf != InvalidOid)
typentry->eq_opr = get_opfamily_member(typentry->btree_opf,
typentry->btree_opintype,
typentry->btree_opintype,
BTEqualStrategyNumber);
if (typentry->eq_opr == InvalidOid &&
typentry->hash_opc != InvalidOid)
typentry->eq_opr = get_opclass_member(typentry->hash_opc,
InvalidOid,
HTEqualStrategyNumber);
typentry->hash_opf != InvalidOid)
typentry->eq_opr = get_opfamily_member(typentry->hash_opf,
typentry->hash_opintype,
typentry->hash_opintype,
HTEqualStrategyNumber);
}
if ((flags & TYPECACHE_LT_OPR) && typentry->lt_opr == InvalidOid)
{
if (typentry->btree_opc != InvalidOid)
typentry->lt_opr = get_opclass_member(typentry->btree_opc,
InvalidOid,
BTLessStrategyNumber);
if (typentry->btree_opf != InvalidOid)
typentry->lt_opr = get_opfamily_member(typentry->btree_opf,
typentry->btree_opintype,
typentry->btree_opintype,
BTLessStrategyNumber);
}
if ((flags & TYPECACHE_GT_OPR) && typentry->gt_opr == InvalidOid)
{
if (typentry->btree_opc != InvalidOid)
typentry->gt_opr = get_opclass_member(typentry->btree_opc,
InvalidOid,
BTGreaterStrategyNumber);
if (typentry->btree_opf != InvalidOid)
typentry->gt_opr = get_opfamily_member(typentry->btree_opf,
typentry->btree_opintype,
typentry->btree_opintype,
BTGreaterStrategyNumber);
}
if ((flags & (TYPECACHE_CMP_PROC | TYPECACHE_CMP_PROC_FINFO)) &&
typentry->cmp_proc == InvalidOid)
{
if (typentry->btree_opc != InvalidOid)
typentry->cmp_proc = get_opclass_proc(typentry->btree_opc,
InvalidOid,
BTORDER_PROC);
if (typentry->btree_opf != InvalidOid)
typentry->cmp_proc = get_opfamily_proc(typentry->btree_opf,
typentry->btree_opintype,
typentry->btree_opintype,
BTORDER_PROC);
}
/*

View File

@@ -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;
}