diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2006-12-23 00:43:13 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2006-12-23 00:43:13 +0000 |
commit | a78fcfb5124379532ce35f3076679f04bd987d60 (patch) | |
tree | 345204410d4f015a9d20ff55ecc38de3d371c459 /src/backend/utils/cache/lsyscache.c | |
parent | d31ccb6c3e73901c44865bfce3f5dd20774f7a89 (diff) |
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.
Diffstat (limited to 'src/backend/utils/cache/lsyscache.c')
-rw-r--r-- | src/backend/utils/cache/lsyscache.c | 413 |
1 files changed, 226 insertions, 187 deletions
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index fdc31e0d977..824ef4a1efe 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -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); @@ -133,10 +138,160 @@ get_opclass_member(Oid opclass, Oid subtype, int16 strategy) } /* + * 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,58 +317,44 @@ 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. */ catlist = SearchSysCacheList(AMOPOPID, 1, @@ -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,67 +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) -{ - 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 == 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) +Oid +get_opclass_family(Oid opclass) { HeapTuple tp; Form_pg_opclass cla_tup; - bool result; + Oid result; tp = SearchSysCache(CLAOID, ObjectIdGetDatum(opclass), @@ -546,7 +631,7 @@ opclass_is_default(Oid opclass) 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) |