diff options
-rw-r--r-- | src/backend/executor/nodeMergejoin.c | 118 | ||||
-rw-r--r-- | src/backend/optimizer/path/costsize.c | 41 | ||||
-rw-r--r-- | src/backend/utils/adt/selfuncs.c | 292 | ||||
-rw-r--r-- | src/backend/utils/cache/lsyscache.c | 72 | ||||
-rw-r--r-- | src/include/utils/lsyscache.h | 4 | ||||
-rw-r--r-- | src/include/utils/selfuncs.h | 10 |
6 files changed, 423 insertions, 114 deletions
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index b0c1eb90a7f..41f0e99693f 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.47 2001/10/28 06:25:43 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.48 2002/03/01 04:09:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -88,97 +88,62 @@ static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext) /* ---------------------------------------------------------------- - * MJFormSkipQual + * MJFormSkipQuals * * This takes the mergeclause which is a qualification of the - * form ((= expr expr) (= expr expr) ...) and forms a new - * qualification like ((> expr expr) (> expr expr) ...) which - * is used by ExecMergeJoin() in order to determine if we should - * skip tuples. The replacement operators are named either ">" - * or "<" according to the replaceopname parameter, and have the - * same operand data types as the "=" operators they replace. - * (We expect there to be such operators because the "=" operators + * form ((= expr expr) (= expr expr) ...) and forms new lists + * of the forms ((< expr expr) (< expr expr) ...) and + * ((> expr expr) (> expr expr) ...). These lists will be used + * by ExecMergeJoin() to determine if we should skip tuples. + * (We expect there to be suitable operators because the "=" operators * were marked mergejoinable; however, there might be a different * one needed in each qual clause.) * ---------------------------------------------------------------- */ -static List * -MJFormSkipQual(List *qualList, char *replaceopname) +static void +MJFormSkipQuals(List *qualList, List **ltQuals, List **gtQuals) { - List *qualCopy; - List *qualcdr; - Expr *qual; - Oper *op; - HeapTuple optup; - Form_pg_operator opform; - Oid oprleft, - oprright; + List *ltcdr, + *gtcdr; /* - * qualList is a list: ((op .. ..) ...) - * - * first we make a copy of it. copyObject() makes a deep copy so let's - * use it instead of the old fashoned lispCopy()... + * Make modifiable copies of the qualList. */ - qualCopy = (List *) copyObject((Node *) qualList); + *ltQuals = (List *) copyObject((Node *) qualList); + *gtQuals = (List *) copyObject((Node *) qualList); - foreach(qualcdr, qualCopy) + /* + * Scan both lists in parallel, so that we can update the operators + * with the minimum number of syscache searches. + */ + ltcdr = *ltQuals; + foreach(gtcdr, *gtQuals) { - /* - * first get the current (op .. ..) list - */ - qual = lfirst(qualcdr); + Expr *ltqual = (Expr *) lfirst(ltcdr); + Expr *gtqual = (Expr *) lfirst(gtcdr); + Oper *ltop = (Oper *) ltqual->oper; + Oper *gtop = (Oper *) gtqual->oper; /* - * now get at the op + * The two ops should be identical, so use either one for lookup. */ - op = (Oper *) qual->oper; - if (!IsA(op, Oper)) - elog(ERROR, "MJFormSkipQual: op not an Oper!"); + if (!IsA(ltop, Oper)) + elog(ERROR, "MJFormSkipQuals: op not an Oper!"); /* - * Get the declared left and right operand types of the operator. - * Note we do *not* use the actual operand types, since those - * might be different in scenarios with binary-compatible data - * types. There should be "<" and ">" operators matching a - * mergejoinable "=" operator's declared operand types, but we - * might not find them if we search with the actual operand types. + * Lookup the operators, and replace the data in the copied + * operator nodes. */ - optup = SearchSysCache(OPEROID, - ObjectIdGetDatum(op->opno), - 0, 0, 0); - if (!HeapTupleIsValid(optup)) /* shouldn't happen */ - elog(ERROR, "MJFormSkipQual: operator %u not found", op->opno); - opform = (Form_pg_operator) GETSTRUCT(optup); - oprleft = opform->oprleft; - oprright = opform->oprright; - ReleaseSysCache(optup); - - /* - * Now look up the matching "<" or ">" operator. If there isn't - * one, whoever marked the "=" operator mergejoinable was a loser. - */ - optup = SearchSysCache(OPERNAME, - PointerGetDatum(replaceopname), - ObjectIdGetDatum(oprleft), - ObjectIdGetDatum(oprright), - CharGetDatum('b')); - if (!HeapTupleIsValid(optup)) - elog(ERROR, - "MJFormSkipQual: mergejoin operator %u has no matching %s op", - op->opno, replaceopname); - opform = (Form_pg_operator) GETSTRUCT(optup); - - /* - * And replace the data in the copied operator node. - */ - op->opno = optup->t_data->t_oid; - op->opid = opform->oprcode; - op->op_fcache = NULL; - ReleaseSysCache(optup); + op_mergejoin_crossops(ltop->opno, + <op->opno, + >op->opno, + <op->opid, + >op->opid); + ltop->op_fcache = NULL; + gtop->op_fcache = NULL; + + ltcdr = lnext(ltcdr); } - - return qualCopy; } /* ---------------------------------------------------------------- @@ -1430,7 +1395,6 @@ bool ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent) { MergeJoinState *mergestate; - List *joinclauses; MJ1_printf("ExecInitMergeJoin: %s\n", "initializing node"); @@ -1522,9 +1486,9 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent) /* * form merge skip qualifications */ - joinclauses = node->mergeclauses; - mergestate->mj_OuterSkipQual = MJFormSkipQual(joinclauses, "<"); - mergestate->mj_InnerSkipQual = MJFormSkipQual(joinclauses, ">"); + MJFormSkipQuals(node->mergeclauses, + &mergestate->mj_OuterSkipQual, + &mergestate->mj_InnerSkipQual); MJ_printf("\nExecInitMergeJoin: OuterSkipQual is "); MJ_nodeDisplay(mergestate->mj_OuterSkipQual); diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 4cfddf9e6ce..e50664cf66b 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -42,7 +42,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.79 2001/10/25 05:49:32 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.80 2002/03/01 04:09:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -58,6 +58,7 @@ #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "parser/parsetree.h" +#include "utils/selfuncs.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -565,12 +566,29 @@ cost_mergejoin(Path *path, Query *root, Cost startup_cost = 0; Cost run_cost = 0; Cost cpu_per_tuple; + double outer_rows, + inner_rows; double ntuples; + Selectivity leftscan, + rightscan; Path sort_path; /* dummy for result of cost_sort */ if (!enable_mergejoin) startup_cost += disable_cost; + /* + * A merge join will stop as soon as it exhausts either input stream. + * Estimate fraction of the left and right inputs that will actually + * need to be scanned. We use only the first (most significant) + * merge clause for this purpose. + */ + mergejoinscansel(root, + (Node *) ((RestrictInfo *) lfirst(mergeclauses))->clause, + &leftscan, &rightscan); + + outer_rows = outer_path->parent->rows * leftscan; + inner_rows = inner_path->parent->rows * rightscan; + /* cost of source data */ /* @@ -588,12 +606,14 @@ cost_mergejoin(Path *path, Query *root, outer_path->parent->rows, outer_path->parent->width); startup_cost += sort_path.startup_cost; - run_cost += sort_path.total_cost - sort_path.startup_cost; + run_cost += (sort_path.total_cost - sort_path.startup_cost) + * leftscan; } else { startup_cost += outer_path->startup_cost; - run_cost += outer_path->total_cost - outer_path->startup_cost; + run_cost += (outer_path->total_cost - outer_path->startup_cost) + * leftscan; } if (innersortkeys) /* do we need to sort inner? */ @@ -605,30 +625,33 @@ cost_mergejoin(Path *path, Query *root, inner_path->parent->rows, inner_path->parent->width); startup_cost += sort_path.startup_cost; - run_cost += sort_path.total_cost - sort_path.startup_cost; + run_cost += (sort_path.total_cost - sort_path.startup_cost) + * rightscan; } else { startup_cost += inner_path->startup_cost; - run_cost += inner_path->total_cost - inner_path->startup_cost; + run_cost += (inner_path->total_cost - inner_path->startup_cost) + * rightscan; } /* * The number of tuple comparisons needed depends drastically on the * number of equal keys in the two source relations, which we have no - * good way of estimating. Somewhat arbitrarily, we charge one tuple + * good way of estimating. (XXX could the MCV statistics help?) + * Somewhat arbitrarily, we charge one tuple * comparison (one cpu_operator_cost) for each tuple in the two source * relations. This is probably a lower bound. */ - run_cost += cpu_operator_cost * - (outer_path->parent->rows + inner_path->parent->rows); + run_cost += cpu_operator_cost * (outer_rows + inner_rows); /* * For each tuple that gets through the mergejoin proper, we charge * cpu_tuple_cost plus the cost of evaluating additional restriction * clauses that are to be applied at the join. It's OK to use an * approximate selectivity here, since in most cases this is a minor - * component of the cost. + * component of the cost. NOTE: it's correct to use the unscaled rows + * counts here, not the scaled-down counts we obtained above. */ ntuples = approx_selectivity(root, mergeclauses) * outer_path->parent->rows * inner_path->parent->rows; diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index edcfe91a47d..6f08deec5a1 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.103 2002/01/03 04:02:34 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.104 2002/03/01 04:09:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -92,11 +92,13 @@ #include "parser/parsetree.h" #include "utils/builtins.h" #include "utils/date.h" +#include "utils/datum.h" #include "utils/int8.h" #include "utils/lsyscache.h" #include "utils/selfuncs.h" #include "utils/syscache.h" + /* * Note: the default selectivity estimates are not chosen entirely at random. * We want them to be small enough to ensure that indexscans will be used if @@ -137,6 +139,7 @@ } while (0) +static bool get_var_maximum(Query *root, Var *var, Oid sortop, Datum *max); static bool convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, Datum lobound, Datum hibound, Oid boundstypid, double *scaledlobound, double *scaledhibound); @@ -419,7 +422,9 @@ neqsel(PG_FUNCTION_ARGS) * * This is the guts of both scalarltsel and scalargtsel. The caller has * commuted the clause, if necessary, so that we can treat the Var as - * being on the left. + * being on the left. The caller must also make sure that the other side + * of the clause is a non-null Const, and dissect same into a value and + * datatype. * * This routine works for any datatype (or pair of datatypes) known to * convert_to_scalar(). If it is applied to some other datatype, @@ -427,11 +432,9 @@ neqsel(PG_FUNCTION_ARGS) */ static double scalarineqsel(Query *root, Oid operator, bool isgt, - Var *var, Node *other) + Var *var, Datum constval, Oid consttype) { Oid relid; - Datum constval; - Oid consttype; HeapTuple statsTuple; Form_pg_statistic stats; FmgrInfo opproc; @@ -454,22 +457,6 @@ scalarineqsel(Query *root, Oid operator, bool isgt, if (relid == InvalidOid) return DEFAULT_INEQ_SEL; - /* - * Can't do anything useful if the something is not a constant, - * either. - */ - if (!IsA(other, Const)) - return DEFAULT_INEQ_SEL; - - /* - * If the constant is NULL, assume operator is strict and return zero, - * ie, operator will never return TRUE. - */ - if (((Const *) other)->constisnull) - return 0.0; - constval = ((Const *) other)->constvalue; - consttype = ((Const *) other)->consttype; - /* get stats for the attribute */ statsTuple = SearchSysCache(STATRELATT, ObjectIdGetDatum(relid), @@ -697,6 +684,8 @@ scalarltsel(PG_FUNCTION_ARGS) int varRelid = PG_GETARG_INT32(3); Var *var; Node *other; + Datum constval; + Oid consttype; bool varonleft; bool isgt; double selec; @@ -711,6 +700,22 @@ scalarltsel(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL); /* + * Can't do anything useful if the something is not a constant, + * either. + */ + if (!IsA(other, Const)) + PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL); + + /* + * If the constant is NULL, assume operator is strict and return zero, + * ie, operator will never return TRUE. + */ + if (((Const *) other)->constisnull) + PG_RETURN_FLOAT8(0.0); + constval = ((Const *) other)->constvalue; + consttype = ((Const *) other)->consttype; + + /* * Force the var to be on the left to simplify logic in scalarineqsel. */ if (varonleft) @@ -730,7 +735,7 @@ scalarltsel(PG_FUNCTION_ARGS) isgt = true; } - selec = scalarineqsel(root, operator, isgt, var, other); + selec = scalarineqsel(root, operator, isgt, var, constval, consttype); PG_RETURN_FLOAT8((float8) selec); } @@ -747,6 +752,8 @@ scalargtsel(PG_FUNCTION_ARGS) int varRelid = PG_GETARG_INT32(3); Var *var; Node *other; + Datum constval; + Oid consttype; bool varonleft; bool isgt; double selec; @@ -761,6 +768,22 @@ scalargtsel(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL); /* + * Can't do anything useful if the something is not a constant, + * either. + */ + if (!IsA(other, Const)) + PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL); + + /* + * If the constant is NULL, assume operator is strict and return zero, + * ie, operator will never return TRUE. + */ + if (((Const *) other)->constisnull) + PG_RETURN_FLOAT8(0.0); + constval = ((Const *) other)->constvalue; + consttype = ((Const *) other)->consttype; + + /* * Force the var to be on the left to simplify logic in scalarineqsel. */ if (varonleft) @@ -780,7 +803,7 @@ scalargtsel(PG_FUNCTION_ARGS) isgt = false; } - selec = scalarineqsel(root, operator, isgt, var, other); + selec = scalarineqsel(root, operator, isgt, var, constval, consttype); PG_RETURN_FLOAT8((float8) selec); } @@ -1696,6 +1719,229 @@ icnlikejoinsel(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(result); } +/* + * mergejoinscansel - Scan selectivity of merge join. + * + * A merge join will stop as soon as it exhausts either input stream. + * Therefore, if we can estimate the ranges of both input variables, + * 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. + * + * *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 + * variable. + */ +void +mergejoinscansel(Query *root, Node *clause, + Selectivity *leftscan, + Selectivity *rightscan) +{ + Var *left, + *right; + Oid opno, + lsortop, + rsortop, + ltop, + gtop, + revltop; + Datum leftmax, + rightmax; + double selec; + + /* Set default results if we can't figure anything out. */ + *leftscan = *rightscan = 1.0; + + /* Deconstruct the merge clause */ + if (!is_opclause(clause)) + return; /* shouldn't happen */ + opno = ((Oper *) ((Expr *) clause)->oper)->opno; + left = get_leftop((Expr *) clause); + right = get_rightop((Expr *) clause); + if (!right) + return; /* shouldn't happen */ + + /* Can't do anything if inputs are not Vars */ + if (!IsA(left, Var) ||!IsA(right, Var)) + return; + + /* Verify mergejoinability and get left and right "<" operators */ + if (!op_mergejoinable(opno, + left->vartype, + right->vartype, + &lsortop, + &rsortop)) + return; /* shouldn't happen */ + + /* Try to get maximum values of both vars */ + if (!get_var_maximum(root, left, lsortop, &leftmax)) + return; /* no max available from stats */ + + if (!get_var_maximum(root, right, rsortop, &rightmax)) + return; /* no max available from stats */ + + /* Look up the "left < right" and "left > right" operators */ + op_mergejoin_crossops(opno, <op, >op, NULL, NULL); + + /* Look up the "right < left" operator */ + revltop = get_commutator(gtop); + if (!OidIsValid(revltop)) + return; /* shouldn't happen */ + + /* + * 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, ltop, false, left, + rightmax, right->vartype); + if (selec != DEFAULT_INEQ_SEL) + *leftscan = selec; + + /* And similarly for the right variable. */ + selec = scalarineqsel(root, revltop, false, right, + leftmax, left->vartype); + if (selec != DEFAULT_INEQ_SEL) + *rightscan = selec; + + /* + * Only one of the two fractions can really be less than 1.0; believe + * the smaller estimate and reset the other one to exactly 1.0. + */ + if (*leftscan > *rightscan) + *leftscan = 1.0; + else + *rightscan = 1.0; +} + +/* + * get_var_maximum + * Estimate the maximum value of the specified variable. + * If successful, store value in *max and return TRUE. + * If no data available, return FALSE. + * + * sortop is the "<" comparison operator to use. (To extract the + * minimum instead of the maximum, just pass the ">" operator instead.) + */ +static bool +get_var_maximum(Query *root, Var *var, Oid sortop, Datum *max) +{ + Datum tmax = 0; + bool have_max = false; + Oid relid; + HeapTuple statsTuple; + Form_pg_statistic stats; + int16 typLen; + bool typByVal; + Datum *values; + int nvalues; + int i; + + relid = getrelid(var->varno, root->rtable); + if (relid == InvalidOid) + return false; + + /* get stats for the attribute */ + statsTuple = SearchSysCache(STATRELATT, + ObjectIdGetDatum(relid), + Int16GetDatum(var->varattno), + 0, 0); + if (!HeapTupleIsValid(statsTuple)) + { + /* no stats available, so default result */ + return false; + } + stats = (Form_pg_statistic) GETSTRUCT(statsTuple); + + get_typlenbyval(var->vartype, &typLen, &typByVal); + + /* + * If there is a histogram, grab the last or first value as appropriate. + * + * If there is a histogram that is sorted with some other operator + * than the one we want, fail --- this suggests that there is data + * we can't use. + */ + if (get_attstatsslot(statsTuple, var->vartype, var->vartypmod, + STATISTIC_KIND_HISTOGRAM, sortop, + &values, &nvalues, + NULL, NULL)) + { + if (nvalues > 0) + { + tmax = datumCopy(values[nvalues-1], typByVal, typLen); + have_max = true; + } + free_attstatsslot(var->vartype, values, nvalues, NULL, 0); + } + else + { + Oid rsortop = get_commutator(sortop); + + if (OidIsValid(rsortop) && + get_attstatsslot(statsTuple, var->vartype, var->vartypmod, + STATISTIC_KIND_HISTOGRAM, rsortop, + &values, &nvalues, + NULL, NULL)) + { + if (nvalues > 0) + { + tmax = datumCopy(values[0], typByVal, typLen); + have_max = true; + } + free_attstatsslot(var->vartype, values, nvalues, NULL, 0); + } + else if (get_attstatsslot(statsTuple, var->vartype, var->vartypmod, + STATISTIC_KIND_HISTOGRAM, InvalidOid, + &values, &nvalues, + NULL, NULL)) + { + free_attstatsslot(var->vartype, values, nvalues, NULL, 0); + ReleaseSysCache(statsTuple); + return false; + } + } + + /* + * If we have most-common-values info, look for a large MCV. This + * is needed even if we also have a histogram, since the histogram + * excludes the MCVs. However, usually the MCVs will not be the + * extreme values, so avoid unnecessary data copying. + */ + if (get_attstatsslot(statsTuple, var->vartype, var->vartypmod, + STATISTIC_KIND_MCV, InvalidOid, + &values, &nvalues, + NULL, NULL)) + { + bool large_mcv = false; + FmgrInfo opproc; + + fmgr_info(get_opcode(sortop), &opproc); + + for (i = 0; i < nvalues; i++) + { + if (!have_max) + { + tmax = values[i]; + large_mcv = have_max = true; + } + else if (DatumGetBool(FunctionCall2(&opproc, tmax, values[i]))) + { + tmax = values[i]; + large_mcv = true; + } + } + if (large_mcv) + tmax = datumCopy(tmax, typByVal, typLen); + free_attstatsslot(var->vartype, values, nvalues, NULL, 0); + } + + ReleaseSysCache(statsTuple); + + *max = tmax; + return have_max; +} /* * convert_to_scalar diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index b359651b9ba..706397d28c6 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 - * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.59 2001/10/25 05:49:46 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.60 2002/03/01 04:09:26 tgl Exp $ * * NOTES * Eventually, the index information should go through here, too. @@ -370,6 +370,76 @@ op_mergejoinable(Oid opno, Oid ltype, Oid rtype, Oid *leftOp, Oid *rightOp) } /* + * 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. + * + * Raises error if operators cannot be found. Assuming that the operator + * had indeed been marked mergejoinable, this indicates that whoever marked + * it so was mistaken. + */ +void +op_mergejoin_crossops(Oid opno, Oid *ltop, Oid *gtop, + RegProcedure *ltproc, RegProcedure *gtproc) +{ + HeapTuple tp; + Form_pg_operator optup; + Oid oprleft, + oprright; + + /* + * Get the declared left and right operand types of the operator. + */ + tp = SearchSysCache(OPEROID, + ObjectIdGetDatum(opno), + 0, 0, 0); + if (!HeapTupleIsValid(tp)) /* shouldn't happen */ + elog(ERROR, "op_mergejoin_crossops: operator %u not found", opno); + optup = (Form_pg_operator) GETSTRUCT(tp); + oprleft = optup->oprleft; + oprright = optup->oprright; + ReleaseSysCache(tp); + + /* + * Look up the "<" operator with the same input types. If there isn't + * one, whoever marked the "=" operator mergejoinable was a loser. + */ + tp = SearchSysCache(OPERNAME, + PointerGetDatum("<"), + ObjectIdGetDatum(oprleft), + ObjectIdGetDatum(oprright), + CharGetDatum('b')); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "op_mergejoin_crossops: mergejoin operator %u has no matching < operator", + opno); + optup = (Form_pg_operator) GETSTRUCT(tp); + *ltop = tp->t_data->t_oid; + if (ltproc) + *ltproc = optup->oprcode; + ReleaseSysCache(tp); + + /* + * And the same for the ">" operator. + */ + tp = SearchSysCache(OPERNAME, + PointerGetDatum(">"), + ObjectIdGetDatum(oprleft), + ObjectIdGetDatum(oprright), + CharGetDatum('b')); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "op_mergejoin_crossops: mergejoin operator %u has no matching > operator", + opno); + optup = (Form_pg_operator) GETSTRUCT(tp); + *gtop = tp->t_data->t_oid; + if (gtproc) + *gtproc = optup->oprcode; + ReleaseSysCache(tp); +} + +/* * op_hashjoinable * * Returns the hash operator corresponding to a hashjoinable operator, diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 97de4559464..c76c8d53d88 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: lsyscache.h,v 1.39 2001/11/05 17:46:36 momjian Exp $ + * $Id: lsyscache.h,v 1.40 2002/03/01 04:09:28 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -29,6 +29,8 @@ extern RegProcedure get_opcode(Oid opno); extern char *get_opname(Oid opno); extern bool op_mergejoinable(Oid opno, Oid ltype, Oid rtype, Oid *leftOp, Oid *rightOp); +extern void op_mergejoin_crossops(Oid opno, Oid *ltop, Oid *gtop, + RegProcedure *ltproc, RegProcedure *gtproc); extern Oid op_hashjoinable(Oid opno, Oid ltype, Oid rtype); extern bool op_iscachable(Oid opno); extern Oid get_commutator(Oid opno); diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h index da5d4d08286..87978ade799 100644 --- a/src/include/utils/selfuncs.h +++ b/src/include/utils/selfuncs.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: selfuncs.h,v 1.4 2001/11/05 17:46:36 momjian Exp $ + * $Id: selfuncs.h,v 1.5 2002/03/01 04:09:28 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -66,8 +66,12 @@ extern Datum icregexnejoinsel(PG_FUNCTION_ARGS); extern Datum nlikejoinsel(PG_FUNCTION_ARGS); extern Datum icnlikejoinsel(PG_FUNCTION_ARGS); -Selectivity booltestsel(Query *root, BooleanTest *clause, int varRelid); -Selectivity nulltestsel(Query *root, NullTest *clause, int varRelid); +extern Selectivity booltestsel(Query *root, BooleanTest *clause, int varRelid); +extern Selectivity nulltestsel(Query *root, NullTest *clause, int varRelid); + +extern void mergejoinscansel(Query *root, Node *clause, + Selectivity *leftscan, + Selectivity *rightscan); extern Datum btcostestimate(PG_FUNCTION_ARGS); extern Datum rtcostestimate(PG_FUNCTION_ARGS); |