diff --git a/doc/src/sgml/xoper.sgml b/doc/src/sgml/xoper.sgml index 395306bbd60e3234d1a257e97775f1466fac9352..24c74cd8b60c96e179c5f2cfb3bcee0f9a6e0f96 100644 --- a/doc/src/sgml/xoper.sgml +++ b/doc/src/sgml/xoper.sgml @@ -1,5 +1,5 @@ @@ -375,6 +375,27 @@ table1.column1 OP table2.column2 equality operators that are (or could be) implemented by memcmp(). + + + The function underlying a hashjoinable operator must be marked + immutable or stable. If it is volatile, the system will never + attempt to use the operator for a hash join. + + + + + + If a hashjoinable operator has an underlying function that is marked + strict, the + function must also be complete: that is, it should return TRUE or + FALSE, never NULL, for any two non-NULL inputs. If this rule is + not followed, hash-optimization of IN operations may + generate wrong results. (Specifically, IN might return + FALSE where the correct answer per spec would be NULL; or it might + yield an error complaining that it wasn't prepared for a NULL result.) + + + @@ -472,6 +493,14 @@ table1.column1 OP table2.column2 + + + The function underlying a mergejoinable operator must be marked + immutable or stable. If it is volatile, the system will never + attempt to use the operator for a merge join. + + + GROUP BY and DISTINCT operations require each diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index e260bc6595077d21a6e21841aeae90412ec94a5a..8663c6c4a147faa47d1ad653b65d6bd09c537a12 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.235 2003/01/10 21:08:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.236 2003/01/15 19:35:35 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1059,6 +1059,8 @@ _copyRestrictInfo(RestrictInfo *from) COPY_NODE_FIELD(subclauseindices); /* XXX probably bad */ COPY_SCALAR_FIELD(eval_cost); COPY_SCALAR_FIELD(this_selec); + COPY_INTLIST_FIELD(left_relids); + COPY_INTLIST_FIELD(right_relids); COPY_SCALAR_FIELD(mergejoinoperator); COPY_SCALAR_FIELD(left_sortop); COPY_SCALAR_FIELD(right_sortop); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 0ef9b3fa2209a20c8d1afae7770c8208ec098a30..a4e9e1092d804781dab5daa978f31d37eeb9c7a3 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.179 2003/01/10 21:08:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.180 2003/01/15 19:35:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -464,10 +464,10 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b) COMPARE_NODE_FIELD(clause); COMPARE_SCALAR_FIELD(ispusheddown); /* - * We ignore subclauseindices, eval_cost, this_selec, left/right_pathkey, - * and left/right_bucketsize, since they may not be set yet, and should be - * derivable from the clause anyway. Probably it's not really necessary - * to compare any of these remaining fields ... + * We ignore subclauseindices, eval_cost, this_selec, left/right_relids, + * left/right_pathkey, and left/right_bucketsize, since they may not be + * set yet, and should be derivable from the clause anyway. Probably it's + * not really necessary to compare any of these remaining fields ... */ COMPARE_SCALAR_FIELD(mergejoinoperator); COMPARE_SCALAR_FIELD(left_sortop); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index e7d8fa71ed78ea0b41c30affb6dce7ee9d13ca14..e72b52570e56b22fa1ec343a5b1b294b02036f05 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.192 2003/01/10 21:08:11 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.193 2003/01/15 19:35:39 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -952,6 +952,8 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node) WRITE_NODE_FIELD(clause); WRITE_BOOL_FIELD(ispusheddown); WRITE_NODE_FIELD(subclauseindices); + WRITE_INTLIST_FIELD(left_relids); + WRITE_INTLIST_FIELD(right_relids); WRITE_OID_FIELD(mergejoinoperator); WRITE_OID_FIELD(left_sortop); WRITE_OID_FIELD(right_sortop); diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c index dd5186860bfe33fb37ed3427e60904ef24ec3177..43b8e99893cef3db4a24d28e5807ffc00e29025e 100644 --- a/src/backend/nodes/print.c +++ b/src/backend/nodes/print.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.58 2002/12/12 15:49:28 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.59 2003/01/15 19:35:39 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -370,10 +370,10 @@ print_expr(Node *expr, List *rtable) { char *opname; - print_expr((Node *) get_leftop(e), rtable); + print_expr(get_leftop(e), rtable); opname = get_opname(((OpExpr *) e)->opno); printf(" %s ", ((opname != NULL) ? opname : "(invalid operator)")); - print_expr((Node *) get_rightop(e), rtable); + print_expr(get_rightop(e), rtable); } else printf("an expr"); diff --git a/src/backend/optimizer/README b/src/backend/optimizer/README index f4d64ebbcadfc5fddcf8cc0b6df359e4faba5b78..955e022d8f6d5bd0b34c838c2b1825c9f9098af1 100644 --- a/src/backend/optimizer/README +++ b/src/backend/optimizer/README @@ -251,8 +251,10 @@ Optimizer Data Structures RelOptInfo - a relation or joined relations - RestrictInfo - restriction clauses, like "x = 3" - JoinInfo - join clauses, including the relids needed for the join + RestrictInfo - WHERE clauses, like "x = 3" or "y = z" + (note the same structure is used for restriction and + join clauses) + JoinInfo - join clauses associated with a particular pair of relations Path - every way to generate a RelOptInfo(sequential,index,joins) SeqScan - a plain Path node with pathtype = T_SeqScan diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c index 0294c828124e4ea9ee07a1337264e3105c8450bc..84041a566d18b3fc7fed3e7d8985143e87475842 100644 --- a/src/backend/optimizer/path/clausesel.c +++ b/src/backend/optimizer/path/clausesel.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.54 2002/12/12 15:49:28 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.55 2003/01/15 19:35:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -266,12 +266,12 @@ addRangeClause(RangeQueryClause **rqlist, Node *clause, if (varonleft) { - var = (Node *) get_leftop((Expr *) clause); + var = get_leftop((Expr *) clause); is_lobound = !isLTsel; /* x < something is high bound */ } else { - var = (Node *) get_rightop((Expr *) clause); + var = get_rightop((Expr *) clause); is_lobound = isLTsel; /* something < x is low bound */ } diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 1d736b34b91102c615c3924a58e3b13148e732d7..efd80dff1ed7a6ffc46b59dee1164ce3198a1ede 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.99 2003/01/12 22:35:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.100 2003/01/15 19:35:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -752,7 +752,6 @@ cost_mergejoin(Path *path, Query *root, Cost cpu_per_tuple; QualCost restrict_qual_cost; RestrictInfo *firstclause; - Var *leftvar; double outer_rows, inner_rows; double ntuples; @@ -779,9 +778,7 @@ cost_mergejoin(Path *path, Query *root, &firstclause->left_mergescansel, &firstclause->right_mergescansel); - leftvar = get_leftop(firstclause->clause); - Assert(IsA(leftvar, Var)); - if (VARISRELMEMBER(leftvar->varno, outer_path->parent)) + if (is_subseti(firstclause->left_relids, outer_path->parent->relids)) { /* left side of clause is outer */ outerscansel = firstclause->left_mergescansel; @@ -935,14 +932,9 @@ cost_hashjoin(Path *path, Query *root, foreach(hcl, hashclauses) { RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(hcl); - Var *left, - *right; Selectivity thisbucketsize; Assert(IsA(restrictinfo, RestrictInfo)); - /* these must be OK, since check_hashjoinable accepted the clause */ - left = get_leftop(restrictinfo->clause); - right = get_rightop(restrictinfo->clause); /* * First we have to figure out which side of the hashjoin clause is the @@ -952,27 +944,30 @@ cost_hashjoin(Path *path, Query *root, * a large query, we cache the bucketsize estimate in the RestrictInfo * node to avoid repeated lookups of statistics. */ - if (VARISRELMEMBER(right->varno, inner_path->parent)) + if (is_subseti(restrictinfo->right_relids, inner_path->parent->relids)) { /* righthand side is inner */ thisbucketsize = restrictinfo->right_bucketsize; if (thisbucketsize < 0) { /* not cached yet */ - thisbucketsize = estimate_hash_bucketsize(root, right, + thisbucketsize = estimate_hash_bucketsize(root, + (Var *) get_rightop(restrictinfo->clause), virtualbuckets); restrictinfo->right_bucketsize = thisbucketsize; } } else { - Assert(VARISRELMEMBER(left->varno, inner_path->parent)); + Assert(is_subseti(restrictinfo->left_relids, + inner_path->parent->relids)); /* lefthand side is inner */ thisbucketsize = restrictinfo->left_bucketsize; if (thisbucketsize < 0) { /* not cached yet */ - thisbucketsize = estimate_hash_bucketsize(root, left, + thisbucketsize = estimate_hash_bucketsize(root, + (Var *) get_leftop(restrictinfo->clause), virtualbuckets); restrictinfo->left_bucketsize = thisbucketsize; } @@ -1088,7 +1083,7 @@ estimate_hash_bucketsize(Query *root, Var *var, int nbuckets) * Lookup info about var's relation and attribute; if none available, * return default estimate. */ - if (!IsA(var, Var)) + if (var == NULL || !IsA(var, Var)) return 0.1; relid = getrelid(var->varno, root->rtable); diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 228a876f71adebd3a88582a0a07c062abab185ec..7e68c41ef37ae5f345461f1e4676457940e0d856 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.130 2002/12/16 21:30:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.131 2003/01/15 19:35:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -85,15 +85,15 @@ static Relids indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index); static Path *make_innerjoin_index_path(Query *root, RelOptInfo *rel, IndexOptInfo *index, List *clausegroup); -static bool match_index_to_operand(int indexkey, Var *operand, +static bool match_index_to_operand(int indexkey, Node *operand, RelOptInfo *rel, IndexOptInfo *index); static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index); static bool match_special_index_operator(Expr *clause, Oid opclass, bool indexkey_on_left); -static List *prefix_quals(Var *leftop, Oid expr_op, +static List *prefix_quals(Node *leftop, Oid expr_op, Const *prefix, Pattern_Prefix_Status pstatus); -static List *network_prefix_quals(Var *leftop, Oid expr_op, Datum rightop); +static List *network_prefix_quals(Node *leftop, Oid expr_op, Datum rightop); static Oid find_operator(const char *opname, Oid datatype); static Datum string_to_datum(const char *str, Oid datatype); static Const *string_to_const(const char *str, Oid datatype); @@ -713,7 +713,7 @@ match_clause_to_indexkey(RelOptInfo *rel, Oid opclass, Expr *clause) { - Var *leftop, + Node *leftop, *rightop; /* Clause must be a binary opclause. */ @@ -730,7 +730,7 @@ match_clause_to_indexkey(RelOptInfo *rel, * Anything that is a "pseudo constant" expression will do. */ if (match_index_to_operand(indexkey, leftop, rel, index) && - is_pseudo_constant_clause((Node *) rightop)) + is_pseudo_constant_clause(rightop)) { if (is_indexable_operator(clause, opclass, true)) return true; @@ -745,7 +745,7 @@ match_clause_to_indexkey(RelOptInfo *rel, } if (match_index_to_operand(indexkey, rightop, rel, index) && - is_pseudo_constant_clause((Node *) leftop)) + is_pseudo_constant_clause(leftop)) { if (is_indexable_operator(clause, opclass, false)) return true; @@ -801,7 +801,7 @@ match_join_clause_to_indexkey(RelOptInfo *rel, Oid opclass, Expr *clause) { - Var *leftop, + Node *leftop, *rightop; /* Clause must be a binary opclause. */ @@ -820,12 +820,12 @@ match_join_clause_to_indexkey(RelOptInfo *rel, */ if (match_index_to_operand(indexkey, leftop, rel, index)) { - List *othervarnos = pull_varnos((Node *) rightop); + List *othervarnos = pull_varnos(rightop); bool isIndexable; isIndexable = !intMember(lfirsti(rel->relids), othervarnos) && - !contain_volatile_functions((Node *) rightop) && + !contain_volatile_functions(rightop) && is_indexable_operator(clause, opclass, true); freeList(othervarnos); return isIndexable; @@ -833,12 +833,12 @@ match_join_clause_to_indexkey(RelOptInfo *rel, if (match_index_to_operand(indexkey, rightop, rel, index)) { - List *othervarnos = pull_varnos((Node *) leftop); + List *othervarnos = pull_varnos(leftop); bool isIndexable; isIndexable = !intMember(lfirsti(rel->relids), othervarnos) && - !contain_volatile_functions((Node *) leftop) && + !contain_volatile_functions(leftop) && is_indexable_operator(clause, opclass, false); freeList(othervarnos); return isIndexable; @@ -1622,7 +1622,7 @@ make_innerjoin_index_path(Query *root, */ static bool match_index_to_operand(int indexkey, - Var *operand, + Node *operand, RelOptInfo *rel, IndexOptInfo *index) { @@ -1633,7 +1633,7 @@ match_index_to_operand(int indexkey, * eval_const_expressions() will have simplified if more than one. */ if (operand && IsA(operand, RelabelType)) - operand = (Var *) ((RelabelType *) operand)->arg; + operand = (Node *) ((RelabelType *) operand)->arg; if (index->indproc == InvalidOid) { @@ -1641,8 +1641,8 @@ match_index_to_operand(int indexkey, * Simple index. */ if (operand && IsA(operand, Var) && - lfirsti(rel->relids) == operand->varno && - indexkey == operand->varattno) + lfirsti(rel->relids) == ((Var *) operand)->varno && + indexkey == ((Var *) operand)->varattno) return true; else return false; @@ -1764,7 +1764,7 @@ match_special_index_operator(Expr *clause, Oid opclass, bool indexkey_on_left) { bool isIndexable = false; - Var *leftop, + Node *leftop, *rightop; Oid expr_op; Const *patt = NULL; @@ -1944,8 +1944,8 @@ expand_indexqual_conditions(List *indexquals) Expr *clause = (Expr *) lfirst(q); /* we know these will succeed */ - Var *leftop = get_leftop(clause); - Var *rightop = get_rightop(clause); + Node *leftop = get_leftop(clause); + Node *rightop = get_rightop(clause); Oid expr_op = ((OpExpr *) clause)->opno; Const *patt = (Const *) rightop; Const *prefix = NULL; @@ -2033,7 +2033,7 @@ expand_indexqual_conditions(List *indexquals) * operators. */ static List * -prefix_quals(Var *leftop, Oid expr_op, +prefix_quals(Node *leftop, Oid expr_op, Const *prefix_const, Pattern_Prefix_Status pstatus) { List *result; @@ -2143,7 +2143,7 @@ prefix_quals(Var *leftop, Oid expr_op, * operator. */ static List * -network_prefix_quals(Var *leftop, Oid expr_op, Datum rightop) +network_prefix_quals(Node *leftop, Oid expr_op, Datum rightop) { bool is_eq; char *opr1name; diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index 65d0d8fa3581216a3a8fe4124fab6183348c5318..8a6fcd3f060215a6a4394e6dbe1447f8122faef6 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.74 2002/11/30 05:21:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.75 2003/01/15 19:35:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -774,10 +774,9 @@ hash_inner_and_outer(Query *root, foreach(i, restrictlist) { RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i); - Var *left, - *right; - if (restrictinfo->hashjoinoperator == InvalidOid) + if (restrictinfo->left_relids == NIL || + restrictinfo->hashjoinoperator == InvalidOid) continue; /* not hashjoinable */ /* @@ -787,26 +786,16 @@ hash_inner_and_outer(Query *root, if (isouterjoin && restrictinfo->ispusheddown) continue; - /* these must be OK, since check_hashjoinable accepted the clause */ - left = get_leftop(restrictinfo->clause); - right = get_rightop(restrictinfo->clause); - /* * Check if clause is usable with these input rels. - * - * Since we currently accept only var-op-var clauses as hashjoinable, - * we need only check the membership of the vars to determine whether - * a particular clause can be used with this pair of sub-relations. - * This code would need to be upgraded if we wanted to allow - * more-complex expressions in hash joins. */ - if (VARISRELMEMBER(left->varno, outerrel) && - VARISRELMEMBER(right->varno, innerrel)) + if (is_subseti(restrictinfo->left_relids, outerrel->relids) && + is_subseti(restrictinfo->right_relids, innerrel->relids)) { /* righthand side is inner */ } - else if (VARISRELMEMBER(left->varno, innerrel) && - VARISRELMEMBER(right->varno, outerrel)) + else if (is_subseti(restrictinfo->left_relids, innerrel->relids) && + is_subseti(restrictinfo->right_relids, outerrel->relids)) { /* lefthand side is inner */ } @@ -874,9 +863,6 @@ select_mergejoin_clauses(RelOptInfo *joinrel, foreach(i, restrictlist) { RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i); - Expr *clause; - Var *left, - *right; /* * If processing an outer join, only use its own join clauses in @@ -896,11 +882,13 @@ select_mergejoin_clauses(RelOptInfo *joinrel, switch (jointype) { case JOIN_RIGHT: - if (restrictinfo->mergejoinoperator == InvalidOid) + if (restrictinfo->left_relids == NIL || + restrictinfo->mergejoinoperator == InvalidOid) return NIL; /* not mergejoinable */ break; case JOIN_FULL: - if (restrictinfo->mergejoinoperator == InvalidOid) + if (restrictinfo->left_relids == NIL || + restrictinfo->mergejoinoperator == InvalidOid) elog(ERROR, "FULL JOIN is only supported with mergejoinable join conditions"); break; default: @@ -909,19 +897,27 @@ select_mergejoin_clauses(RelOptInfo *joinrel, } } - if (restrictinfo->mergejoinoperator == InvalidOid) + if (restrictinfo->left_relids == NIL || + restrictinfo->mergejoinoperator == InvalidOid) continue; /* not mergejoinable */ - clause = restrictinfo->clause; - /* these must be OK, since check_mergejoinable accepted the clause */ - left = get_leftop(clause); - right = get_rightop(clause); + /* + * Check if clause is usable with these input rels. + */ + if (is_subseti(restrictinfo->left_relids, outerrel->relids) && + is_subseti(restrictinfo->right_relids, innerrel->relids)) + { + /* righthand side is inner */ + } + else if (is_subseti(restrictinfo->left_relids, innerrel->relids) && + is_subseti(restrictinfo->right_relids, outerrel->relids)) + { + /* lefthand side is inner */ + } + else + continue; /* no good for these input relations */ - if ((VARISRELMEMBER(left->varno, outerrel) && - VARISRELMEMBER(right->varno, innerrel)) || - (VARISRELMEMBER(left->varno, innerrel) && - VARISRELMEMBER(right->varno, outerrel))) - result_list = lcons(restrictinfo, result_list); + result_list = lcons(restrictinfo, result_list); } return result_list; diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 9c6ed4d1bd35422c2ffb69ca16f9a46f03e41eae..194bdddc2f09c1b151b3f64053331e9c3b0e7157 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -11,7 +11,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.43 2002/12/17 01:18:22 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.44 2003/01/15 19:35:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,27 +53,23 @@ makePathKeyItem(Node *key, Oid sortop) * The given clause has a mergejoinable operator, so its two sides * can be considered equal after restriction clause application; in * particular, any pathkey mentioning one side (with the correct sortop) - * can be expanded to include the other as well. Record the vars and + * can be expanded to include the other as well. Record the exprs and * associated sortops in the query's equi_key_list for future use. * * The query's equi_key_list field points to a list of sublists of PathKeyItem - * nodes, where each sublist is a set of two or more vars+sortops that have + * nodes, where each sublist is a set of two or more exprs+sortops that have * been identified as logically equivalent (and, therefore, we may consider * any two in a set to be equal). As described above, we will subsequently * use direct pointers to one of these sublists to represent any pathkey * that involves an equijoined variable. - * - * This code would actually work fine with expressions more complex than - * a single Var, but currently it won't see any because check_mergejoinable - * won't accept such clauses as mergejoinable. */ void add_equijoined_keys(Query *root, RestrictInfo *restrictinfo) { Expr *clause = restrictinfo->clause; - PathKeyItem *item1 = makePathKeyItem((Node *) get_leftop(clause), + PathKeyItem *item1 = makePathKeyItem(get_leftop(clause), restrictinfo->left_sortop); - PathKeyItem *item2 = makePathKeyItem((Node *) get_rightop(clause), + PathKeyItem *item2 = makePathKeyItem(get_rightop(clause), restrictinfo->right_sortop); List *newset, *cursetlink; @@ -717,13 +713,13 @@ cache_mergeclause_pathkeys(Query *root, RestrictInfo *restrictinfo) if (restrictinfo->left_pathkey == NIL) { - key = (Node *) get_leftop(restrictinfo->clause); + key = get_leftop(restrictinfo->clause); item = makePathKeyItem(key, restrictinfo->left_sortop); restrictinfo->left_pathkey = make_canonical_pathkey(root, item); } if (restrictinfo->right_pathkey == NIL) { - key = (Node *) get_rightop(restrictinfo->clause); + key = get_rightop(restrictinfo->clause); item = makePathKeyItem(key, restrictinfo->right_sortop); restrictinfo->right_pathkey = make_canonical_pathkey(root, item); } @@ -852,32 +848,24 @@ make_pathkeys_for_mergeclauses(Query *root, foreach(i, mergeclauses) { RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i); - Node *key; List *pathkey; cache_mergeclause_pathkeys(root, restrictinfo); - key = (Node *) get_leftop(restrictinfo->clause); - if (IsA(key, Var) && - VARISRELMEMBER(((Var *) key)->varno, rel)) + if (is_subseti(restrictinfo->left_relids, rel->relids)) { /* Rel is left side of mergeclause */ pathkey = restrictinfo->left_pathkey; } + else if (is_subseti(restrictinfo->right_relids, rel->relids)) + { + /* Rel is right side of mergeclause */ + pathkey = restrictinfo->right_pathkey; + } else { - key = (Node *) get_rightop(restrictinfo->clause); - if (IsA(key, Var) && - VARISRELMEMBER(((Var *) key)->varno, rel)) - { - /* Rel is right side of mergeclause */ - pathkey = restrictinfo->right_pathkey; - } - else - { - elog(ERROR, "make_pathkeys_for_mergeclauses: can't identify which side of mergeclause to use"); - pathkey = NIL; /* keep compiler quiet */ - } + elog(ERROR, "make_pathkeys_for_mergeclauses: can't identify which side of mergeclause to use"); + pathkey = NIL; /* keep compiler quiet */ } /* diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 3d9ee6e4f4f6313c9166c36dcf59880f2acd4c2f..03ec35384797f41dfb0a5618f3dbfaf146b6ee93 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.129 2003/01/13 00:29:25 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.130 2003/01/15 19:35:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -49,18 +49,15 @@ static FunctionScan *create_functionscan_plan(Path *best_path, static NestLoop *create_nestloop_plan(Query *root, NestPath *best_path, List *tlist, List *joinclauses, List *otherclauses, - Plan *outer_plan, List *outer_tlist, - Plan *inner_plan, List *inner_tlist); + Plan *outer_plan, Plan *inner_plan); static MergeJoin *create_mergejoin_plan(Query *root, MergePath *best_path, List *tlist, List *joinclauses, List *otherclauses, - Plan *outer_plan, List *outer_tlist, - Plan *inner_plan, List *inner_tlist); + Plan *outer_plan, Plan *inner_plan); static HashJoin *create_hashjoin_plan(Query *root, HashPath *best_path, List *tlist, List *joinclauses, List *otherclauses, - Plan *outer_plan, List *outer_tlist, - Plan *inner_plan, List *inner_tlist); + Plan *outer_plan, Plan *inner_plan); static void fix_indxqual_references(List *indexquals, IndexPath *index_path, List **fixed_indexquals, List **recheck_indexquals); @@ -70,7 +67,7 @@ static void fix_indxqual_sublist(List *indexqual, int baserelid, static Node *fix_indxqual_operand(Node *node, int baserelid, IndexOptInfo *index, Oid *opclass); -static List *switch_outer(List *clauses); +static List *get_switched_clauses(List *clauses, List *outerrelids); static List *order_qual_clauses(Query *root, List *clauses); static void copy_path_costsize(Plan *dest, Path *src); static void copy_plan_costsize(Plan *dest, Plan *src); @@ -98,6 +95,9 @@ static MergeJoin *make_mergejoin(List *tlist, List *mergeclauses, Plan *lefttree, Plan *righttree, JoinType jointype); +static Sort *make_sort_from_pathkeys(Query *root, Plan *lefttree, + List *relids, List *pathkeys); + /* * create_plan @@ -246,18 +246,13 @@ create_join_plan(Query *root, JoinPath *best_path) { List *join_tlist = best_path->path.parent->targetlist; Plan *outer_plan; - List *outer_tlist; Plan *inner_plan; - List *inner_tlist; List *joinclauses; List *otherclauses; Join *plan; outer_plan = create_plan(root, best_path->outerjoinpath); - outer_tlist = outer_plan->targetlist; - inner_plan = create_plan(root, best_path->innerjoinpath); - inner_tlist = inner_plan->targetlist; if (IS_OUTER_JOIN(best_path->jointype)) { @@ -280,9 +275,7 @@ create_join_plan(Query *root, JoinPath *best_path) joinclauses, otherclauses, outer_plan, - outer_tlist, - inner_plan, - inner_tlist); + inner_plan); break; case T_HashJoin: plan = (Join *) create_hashjoin_plan(root, @@ -291,9 +284,7 @@ create_join_plan(Query *root, JoinPath *best_path) joinclauses, otherclauses, outer_plan, - outer_tlist, - inner_plan, - inner_tlist); + inner_plan); break; case T_NestLoop: plan = (Join *) create_nestloop_plan(root, @@ -302,9 +293,7 @@ create_join_plan(Query *root, JoinPath *best_path) joinclauses, otherclauses, outer_plan, - outer_tlist, - inner_plan, - inner_tlist); + inner_plan); break; default: elog(ERROR, "create_join_plan: unknown node type: %d", @@ -672,10 +661,9 @@ create_functionscan_plan(Path *best_path, List *tlist, List *scan_clauses) * * A cleaner solution would be to not call join_references() here at all, * but leave it for setrefs.c to do at the end of plan tree construction. - * But that would make switch_outer() much more complicated, and some care - * would be needed to get setrefs.c to do the right thing with nestloop - * inner indexscan quals. So, we do subplan reference adjustment here for - * quals of join nodes (and *only* for quals of join nodes). + * But some care would be needed to get setrefs.c to do the right thing with + * nestloop inner indexscan quals. So, we do subplan reference adjustment + * here for quals of join nodes (and *only* for quals of join nodes). * *****************************************************************************/ @@ -686,10 +674,10 @@ create_nestloop_plan(Query *root, List *joinclauses, List *otherclauses, Plan *outer_plan, - List *outer_tlist, - Plan *inner_plan, - List *inner_tlist) + Plan *inner_plan) { + List *outer_tlist = outer_plan->targetlist; + List *inner_tlist = inner_plan->targetlist; NestLoop *join_plan; if (IsA(inner_plan, IndexScan)) @@ -797,44 +785,45 @@ create_mergejoin_plan(Query *root, List *joinclauses, List *otherclauses, Plan *outer_plan, - List *outer_tlist, - Plan *inner_plan, - List *inner_tlist) + Plan *inner_plan) { + List *outer_tlist = outer_plan->targetlist; + List *inner_tlist = inner_plan->targetlist; List *mergeclauses; MergeJoin *join_plan; + /* + * Remove the mergeclauses from the list of join qual clauses, leaving + * the list of quals that must be checked as qpquals. + */ mergeclauses = get_actual_clauses(best_path->path_mergeclauses); + joinclauses = set_difference(joinclauses, mergeclauses); /* - * Remove the mergeclauses from the list of join qual clauses, leaving - * the list of quals that must be checked as qpquals. Set those - * clauses to contain INNER/OUTER var references. + * Rearrange mergeclauses, if needed, so that the outer variable + * is always on the left. */ - joinclauses = join_references(set_difference(joinclauses, mergeclauses), + mergeclauses = get_switched_clauses(best_path->path_mergeclauses, + best_path->jpath.outerjoinpath->parent->relids); + + /* + * Fix all the join clauses to contain INNER/OUTER var references. + */ + joinclauses = join_references(joinclauses, root->rtable, outer_tlist, inner_tlist, (Index) 0); - - /* - * Fix the additional qpquals too. - */ otherclauses = join_references(otherclauses, root->rtable, outer_tlist, inner_tlist, (Index) 0); - - /* - * Now set the references in the mergeclauses and rearrange them so - * that the outer variable is always on the left. - */ - mergeclauses = switch_outer(join_references(mergeclauses, - root->rtable, - outer_tlist, - inner_tlist, - (Index) 0)); + mergeclauses = join_references(mergeclauses, + root->rtable, + outer_tlist, + inner_tlist, + (Index) 0); /* * Create explicit sort nodes for the outer and inner join paths if @@ -843,15 +832,15 @@ create_mergejoin_plan(Query *root, if (best_path->outersortkeys) outer_plan = (Plan *) make_sort_from_pathkeys(root, - outer_tlist, outer_plan, + best_path->jpath.outerjoinpath->parent->relids, best_path->outersortkeys); if (best_path->innersortkeys) inner_plan = (Plan *) make_sort_from_pathkeys(root, - inner_tlist, inner_plan, + best_path->jpath.innerjoinpath->parent->relids, best_path->innersortkeys); /* @@ -877,47 +866,48 @@ create_hashjoin_plan(Query *root, List *joinclauses, List *otherclauses, Plan *outer_plan, - List *outer_tlist, - Plan *inner_plan, - List *inner_tlist) + Plan *inner_plan) { + List *outer_tlist = outer_plan->targetlist; + List *inner_tlist = inner_plan->targetlist; List *hashclauses; HashJoin *join_plan; Hash *hash_plan; List *innerhashkeys; List *hcl; + /* + * Remove the hashclauses from the list of join qual clauses, leaving + * the list of quals that must be checked as qpquals. + */ hashclauses = get_actual_clauses(best_path->path_hashclauses); + joinclauses = set_difference(joinclauses, hashclauses); /* - * Remove the hashclauses from the list of join qual clauses, leaving - * the list of quals that must be checked as qpquals. Set those - * clauses to contain INNER/OUTER var references. + * Rearrange hashclauses, if needed, so that the outer variable + * is always on the left. + */ + hashclauses = get_switched_clauses(best_path->path_hashclauses, + best_path->jpath.outerjoinpath->parent->relids); + + /* + * Fix all the join clauses to contain INNER/OUTER var references. */ - joinclauses = join_references(set_difference(joinclauses, hashclauses), + joinclauses = join_references(joinclauses, root->rtable, outer_tlist, inner_tlist, (Index) 0); - - /* - * Fix the additional qpquals too. - */ otherclauses = join_references(otherclauses, root->rtable, outer_tlist, inner_tlist, (Index) 0); - - /* - * Now set the references in the hashclauses and rearrange them so - * that the outer variable is always on the left. - */ - hashclauses = switch_outer(join_references(hashclauses, - root->rtable, - outer_tlist, - inner_tlist, - (Index) 0)); + hashclauses = join_references(hashclauses, + root->rtable, + outer_tlist, + inner_tlist, + (Index) 0); /* * Extract the inner hash keys (right-hand operands of the hashclauses) @@ -1154,27 +1144,26 @@ fix_indxqual_operand(Node *node, int baserelid, IndexOptInfo *index, } /* - * switch_outer - * Given a list of merge or hash joinclauses, rearrange the elements within - * the clauses so the outer join variable is on the left and the inner is - * on the right. The original list is not touched; a modified list - * is returned. + * get_switched_clauses + * Given a list of merge or hash joinclauses (as RestrictInfo nodes), + * extract the bare clauses, and rearrange the elements within the + * clauses, if needed, so the outer join variable is on the left and + * the inner is on the right. The original data structure is not touched; + * a modified list is returned. */ static List * -switch_outer(List *clauses) +get_switched_clauses(List *clauses, List *outerrelids) { List *t_list = NIL; List *i; foreach(i, clauses) { - OpExpr *clause = (OpExpr *) lfirst(i); - Var *op; + RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i); + OpExpr *clause = (OpExpr *) restrictinfo->clause; Assert(is_opclause(clause)); - op = get_rightop((Expr *) clause); - Assert(op && IsA(op, Var)); - if (var_is_outer(op)) + if (is_subseti(restrictinfo->right_relids, outerrelids)) { /* * Duplicate just enough of the structure to allow commuting @@ -1554,17 +1543,24 @@ make_sort(Query *root, List *tlist, Plan *lefttree, int keycount) * make_sort_from_pathkeys * Create sort plan to sort according to given pathkeys * - * 'tlist' is the target list of the input plan * 'lefttree' is the node which yields input tuples + * 'relids' is the set of relids represented by the input node * 'pathkeys' is the list of pathkeys by which the result is to be sorted * * We must convert the pathkey information into reskey and reskeyop fields * of resdom nodes in the sort plan's target list. + * + * If the pathkeys include expressions that aren't simple Vars, we will + * usually need to add resjunk items to the input plan's targetlist to + * compute these expressions (since the Sort node itself won't do it). + * If the input plan type isn't one that can do projections, this means + * adding a Result node just to do the projection. */ -Sort * -make_sort_from_pathkeys(Query *root, List *tlist, - Plan *lefttree, List *pathkeys) +static Sort * +make_sort_from_pathkeys(Query *root, Plan *lefttree, + List *relids, List *pathkeys) { + List *tlist = lefttree->targetlist; List *sort_tlist; List *i; int numsortkeys = 0; @@ -1582,7 +1578,8 @@ make_sort_from_pathkeys(Query *root, List *tlist, /* * We can sort by any one of the sort key items listed in this * sublist. For now, we take the first one that corresponds to an - * available Var in the sort_tlist. + * available Var in the sort_tlist. If there isn't any, use the + * first one that is an expression in the input's vars. * * XXX if we have a choice, is there any way of figuring out which * might be cheapest to execute? (For example, int4lt is likely @@ -1599,8 +1596,52 @@ make_sort_from_pathkeys(Query *root, List *tlist, break; } if (!resdom) - elog(ERROR, "make_sort_from_pathkeys: cannot find tlist item to sort"); - + { + /* No matching Var; look for an expression */ + foreach(j, keysublist) + { + pathkey = lfirst(j); + if (is_subseti(pull_varnos(pathkey->key), relids)) + break; + } + if (!j) + elog(ERROR, "make_sort_from_pathkeys: cannot find pathkey item to sort"); + /* + * Do we need to insert a Result node? + * + * Currently, the only non-projection-capable plan type + * we can see here is Append. + */ + if (IsA(lefttree, Append)) + { + tlist = new_unsorted_tlist(tlist); + lefttree = (Plan *) make_result(tlist, NULL, lefttree); + } + /* + * Add resjunk entry to input's tlist + */ + resdom = makeResdom(length(tlist) + 1, + exprType(pathkey->key), + exprTypmod(pathkey->key), + NULL, + true); + tlist = lappend(tlist, + makeTargetEntry(resdom, + (Expr *) pathkey->key)); + lefttree->targetlist = tlist; /* just in case NIL before */ + /* + * Add one to sort node's tlist too. This will be identical + * except we are going to set the sort key info in it. + */ + resdom = makeResdom(length(sort_tlist) + 1, + exprType(pathkey->key), + exprTypmod(pathkey->key), + NULL, + true); + sort_tlist = lappend(sort_tlist, + makeTargetEntry(resdom, + (Expr *) pathkey->key)); + } /* * The resdom might be already marked as a sort key, if the * pathkeys contain duplicate entries. (This can happen in diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 1c1a848ea72dcc8be4c7b6ec0264f0d596a0352a..87c77e52fc37e2f063adfacbf261c89dcf42a47f 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.80 2003/01/12 22:35:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.81 2003/01/15 19:35:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -60,32 +60,24 @@ static void check_hashjoinable(RestrictInfo *restrictinfo); * add_base_rels_to_query * * Scan the query's jointree and create baserel RelOptInfos for all - * the base relations (ie, table and subquery RTEs) appearing in the - * jointree. Also, create otherrel RelOptInfos for join RTEs. - * - * The return value is a list of all the baserel indexes (but not join RTE - * indexes) included in the scanned jointree. This is actually just an - * internal convenience for marking join otherrels properly; no outside - * caller uses the result. + * the base relations (ie, table, subquery, and function RTEs) + * appearing in the jointree. * * At the end of this process, there should be one baserel RelOptInfo for * every non-join RTE that is used in the query. Therefore, this routine * is the only place that should call build_base_rel. But build_other_rel - * will be used again later to build rels for inheritance children. + * will be used later to build rels for inheritance children. */ -List * +void add_base_rels_to_query(Query *root, Node *jtnode) { - List *result = NIL; - if (jtnode == NULL) - return NIL; + return; if (IsA(jtnode, RangeTblRef)) { int varno = ((RangeTblRef *) jtnode)->rtindex; build_base_rel(root, varno); - result = makeListi1(varno); } else if (IsA(jtnode, FromExpr)) { @@ -94,29 +86,15 @@ add_base_rels_to_query(Query *root, Node *jtnode) foreach(l, f->fromlist) { - result = nconc(result, - add_base_rels_to_query(root, lfirst(l))); + add_base_rels_to_query(root, lfirst(l)); } } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; - RelOptInfo *jrel; - - result = add_base_rels_to_query(root, j->larg); - result = nconc(result, - add_base_rels_to_query(root, j->rarg)); - /* the join's own rtindex is NOT added to result */ - jrel = build_other_rel(root, j->rtindex); - - /* - * Mark the join's otherrel with outerjoinset = list of baserel - * ids included in the join. Note we must copy here because - * result list is destructively modified by nconcs at higher - * levels. - */ - jrel->outerjoinset = listCopy(result); + add_base_rels_to_query(root, j->larg); + add_base_rels_to_query(root, j->rarg); /* * Safety check: join RTEs should not be SELECT FOR UPDATE targets */ @@ -126,7 +104,6 @@ add_base_rels_to_query(Query *root, Node *jtnode) else elog(ERROR, "add_base_rels_to_query: unexpected node type %d", nodeTag(jtnode)); - return result; } @@ -154,11 +131,6 @@ build_base_rel_tlists(Query *root, List *tlist) * add_vars_to_targetlist * For each variable appearing in the list, add it to the owning * relation's targetlist if not already present. - * - * Note that join alias variables will be attached to the otherrel for - * the join RTE. They will later be transferred to the tlist of - * the corresponding joinrel. We will also cause entries to be made - * for the Vars that the alias will eventually depend on. */ static void add_vars_to_targetlist(Query *root, List *vars) @@ -171,19 +143,6 @@ add_vars_to_targetlist(Query *root, List *vars) RelOptInfo *rel = find_base_rel(root, var->varno); add_var_to_tlist(rel, var); - - if (rel->reloptkind == RELOPT_OTHER_JOIN_REL) - { - /* Var is an alias */ - Node *expansion; - List *varsused; - - expansion = flatten_join_alias_vars((Node *) var, - root->rtable, true); - varsused = pull_var_clause(expansion, false); - add_vars_to_targetlist(root, varsused); - freeList(varsused); - } } } @@ -398,6 +357,8 @@ distribute_qual_to_rels(Query *root, Node *clause, restrictinfo->subclauseindices = NIL; restrictinfo->eval_cost.startup = -1; /* not computed until needed */ restrictinfo->this_selec = -1; /* not computed until needed */ + restrictinfo->left_relids = NIL; /* set below, if join clause */ + restrictinfo->right_relids = NIL; restrictinfo->mergejoinoperator = InvalidOid; restrictinfo->left_sortop = InvalidOid; restrictinfo->right_sortop = InvalidOid; @@ -416,41 +377,11 @@ distribute_qual_to_rels(Query *root, Node *clause, clause_get_relids_vars(clause, &relids, &vars); /* - * The clause might contain some join alias vars; if so, we want to - * remove the join otherrelids from relids and add the referent joins' - * scope lists instead (thus ensuring that the clause can be evaluated - * no lower than that join node). We rely here on the marking done - * earlier by add_base_rels_to_query. - * - * We can combine this step with a cross-check that the clause contains - * no relids not within its scope. If the first crosscheck succeeds, - * the clause contains no aliases and we needn't look more closely. + * Cross-check: clause should contain no relids not within its scope. + * Otherwise the parser messed up. */ if (!is_subseti(relids, qualscope)) - { - Relids newrelids = NIL; - List *relid; - - foreach(relid, relids) - { - RelOptInfo *rel = find_other_rel(root, lfirsti(relid)); - - if (rel && rel->outerjoinset) - { - /* this relid is for a join RTE */ - newrelids = set_unioni(newrelids, rel->outerjoinset); - } - else - { - /* this relid is for a true baserel */ - newrelids = lappendi(newrelids, lfirsti(relid)); - } - } - relids = newrelids; - /* Now repeat the crosscheck */ - if (!is_subseti(relids, qualscope)) - elog(ERROR, "JOIN qualification may not refer to other relations"); - } + elog(ERROR, "JOIN qualification may not refer to other relations"); /* * If the clause is variable-free, we force it to be evaluated at its @@ -575,7 +506,27 @@ distribute_qual_to_rels(Query *root, Node *clause, /* * 'clause' is a join clause, since there is more than one rel in * the relid list. Set additional RestrictInfo fields for - * joining. + * joining. First, does it look like a normal join clause, i.e., + * a binary operator relating expressions that come from distinct + * relations? If so we might be able to use it in a join algorithm. + */ + if (is_opclause(clause) && length(((OpExpr *) clause)->args) == 2) + { + List *left_relids; + List *right_relids; + + left_relids = pull_varnos(get_leftop((Expr *) clause)); + right_relids = pull_varnos(get_rightop((Expr *) clause)); + if (left_relids && right_relids && + nonoverlap_setsi(left_relids, right_relids)) + { + restrictinfo->left_relids = left_relids; + restrictinfo->right_relids = right_relids; + } + } + + /* + * Now check for hash or mergejoinable operators. * * We don't bother setting the hashjoin info if we're not going * to need it. We do want to know about mergejoinable ops in all @@ -675,11 +626,6 @@ void process_implied_equality(Query *root, Node *item1, Node *item2, Oid sortop1, Oid sortop2) { - Index irel1; - Index irel2; - RelOptInfo *rel1; - List *restrictlist; - List *itm; Oid ltype, rtype; Operator eq_operator; @@ -687,50 +633,14 @@ process_implied_equality(Query *root, Node *item1, Node *item2, Expr *clause; /* - * Currently, since check_mergejoinable only accepts Var = Var - * clauses, we should only see Var nodes here. Would have to work a - * little harder to locate the right rel(s) if more-general mergejoin - * clauses were accepted. - */ - Assert(IsA(item1, Var)); - irel1 = ((Var *) item1)->varno; - Assert(IsA(item2, Var)); - irel2 = ((Var *) item2)->varno; - - /* - * If both vars belong to same rel, we need to look at that rel's - * baserestrictinfo list. If different rels, each will have a - * joininfo node for the other, and we can scan either list. - */ - rel1 = find_base_rel(root, irel1); - if (irel1 == irel2) - restrictlist = rel1->baserestrictinfo; - else - { - JoinInfo *joininfo = find_joininfo_node(rel1, - makeListi1(irel2)); - - restrictlist = joininfo->jinfo_restrictinfo; - } - - /* - * Scan to see if equality is already known. + * Forget it if this equality is already recorded. + * + * Note: if only a single relation is involved, we may fall through + * here and end up rejecting the equality later on in qual_is_redundant. + * This is a tad slow but should be okay. */ - foreach(itm, restrictlist) - { - RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(itm); - Node *left, - *right; - - if (restrictinfo->mergejoinoperator == InvalidOid) - continue; /* ignore non-mergejoinable clauses */ - /* We now know the restrictinfo clause is a binary opclause */ - left = (Node *) get_leftop(restrictinfo->clause); - right = (Node *) get_rightop(restrictinfo->clause); - if ((equal(item1, left) && equal(item2, right)) || - (equal(item2, left) && equal(item1, right))) - return; /* found a matching clause */ - } + if (exprs_known_equal(root, item1, item2)) + return; /* * This equality is new information, so construct a clause @@ -770,6 +680,8 @@ process_implied_equality(Query *root, Node *item1, Node *item2, ReleaseSysCache(eq_operator); /* + * Push the new clause into all the appropriate restrictinfo lists. + * * Note: we mark the qual "pushed down" to ensure that it can never be * taken for an original JOIN/ON clause. */ @@ -779,44 +691,45 @@ process_implied_equality(Query *root, Node *item1, Node *item2, } /* - * vars_known_equal - * Detect whether two Vars are known equal due to equijoin clauses. + * exprs_known_equal + * Detect whether two expressions are known equal due to equijoin clauses. * * This is not completely accurate since we avoid adding redundant restriction * clauses to individual base rels (see qual_is_redundant). However, after - * the implied-equality-deduction phase, it is complete for Vars of different - * rels; that's sufficient for planned uses. + * the implied-equality-deduction phase, it is complete for expressions + * involving Vars of multiple rels; that's sufficient for planned uses. */ bool -vars_known_equal(Query *root, Var *var1, Var *var2) +exprs_known_equal(Query *root, Node *item1, Node *item2) { - Index irel1; - Index irel2; + List *relids; RelOptInfo *rel1; List *restrictlist; List *itm; + /* Get list of relids referenced in the two expressions */ + relids = set_unioni(pull_varnos(item1), pull_varnos(item2)); + /* - * Would need more work here if we wanted to check for known equality - * of general clauses: there might be multiple base rels involved. + * If there are no Vars at all, say "true". This prevents + * process_implied_equality from trying to store "const = const" + * deductions. */ - Assert(IsA(var1, Var)); - irel1 = var1->varno; - Assert(IsA(var2, Var)); - irel2 = var2->varno; + if (relids == NIL) + return true; /* - * If both vars belong to same rel, we need to look at that rel's - * baserestrictinfo list. If different rels, each will have a - * joininfo node for the other, and we can scan either list. + * If the exprs involve a single rel, we need to look at that rel's + * baserestrictinfo list. If multiple rels, any one will have a + * joininfo node for the rest, and we can scan any of 'em. */ - rel1 = find_base_rel(root, irel1); - if (irel1 == irel2) + rel1 = find_base_rel(root, lfirsti(relids)); + relids = lnext(relids); + if (relids == NIL) restrictlist = rel1->baserestrictinfo; else { - JoinInfo *joininfo = find_joininfo_node(rel1, - makeListi1(irel2)); + JoinInfo *joininfo = find_joininfo_node(rel1, relids); restrictlist = joininfo->jinfo_restrictinfo; } @@ -833,10 +746,10 @@ vars_known_equal(Query *root, Var *var1, Var *var2) if (restrictinfo->mergejoinoperator == InvalidOid) continue; /* ignore non-mergejoinable clauses */ /* We now know the restrictinfo clause is a binary opclause */ - left = (Node *) get_leftop(restrictinfo->clause); - right = (Node *) get_rightop(restrictinfo->clause); - if ((equal(var1, left) && equal(var2, right)) || - (equal(var2, left) && equal(var1, right))) + left = get_leftop(restrictinfo->clause); + right = get_rightop(restrictinfo->clause); + if ((equal(item1, left) && equal(item2, right)) || + (equal(item2, left) && equal(item1, right))) return true; /* found a matching clause */ } @@ -862,7 +775,7 @@ qual_is_redundant(Query *root, List *olditem; Node *newleft; Node *newright; - List *equalvars; + List *equalexprs; bool someadded; /* @@ -898,15 +811,15 @@ qual_is_redundant(Query *root, return false; /* - * Now, we want to develop a list of Vars that are known equal to the + * Now, we want to develop a list of exprs that are known equal to the * left side of the new qual. We traverse the old-quals list - * repeatedly to transitively expand the Vars list. If at any point - * we find we can reach the right-side Var of the new qual, we are - * done. We give up when we can't expand the equalvars list any more. + * repeatedly to transitively expand the exprs list. If at any point + * we find we can reach the right-side expr of the new qual, we are + * done. We give up when we can't expand the equalexprs list any more. */ - newleft = (Node *) get_leftop(restrictinfo->clause); - newright = (Node *) get_rightop(restrictinfo->clause); - equalvars = makeList1(newleft); + newleft = get_leftop(restrictinfo->clause); + newright = get_rightop(restrictinfo->clause); + equalexprs = makeList1(newleft); do { someadded = false; @@ -915,22 +828,22 @@ qual_is_redundant(Query *root, while (olditem) { RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem); - Node *oldleft = (Node *) get_leftop(oldrinfo->clause); - Node *oldright = (Node *) get_rightop(oldrinfo->clause); + Node *oldleft = get_leftop(oldrinfo->clause); + Node *oldright = get_rightop(oldrinfo->clause); Node *newguy = NULL; /* must advance olditem before lremove possibly pfree's it */ olditem = lnext(olditem); - if (member(oldleft, equalvars)) + if (member(oldleft, equalexprs)) newguy = oldright; - else if (member(oldright, equalvars)) + else if (member(oldright, equalexprs)) newguy = oldleft; else continue; if (equal(newguy, newright)) return true; /* we proved new clause is redundant */ - equalvars = lcons(newguy, equalvars); + equalexprs = lcons(newguy, equalexprs); someadded = true; /* @@ -956,39 +869,28 @@ qual_is_redundant(Query *root, * info fields in the restrictinfo. * * Currently, we support mergejoin for binary opclauses where - * both operands are simple Vars and the operator is a mergejoinable - * operator. + * the operator is a mergejoinable operator. The arguments can be + * anything --- as long as there are no volatile functions in them. */ static void check_mergejoinable(RestrictInfo *restrictinfo) { Expr *clause = restrictinfo->clause; - Var *left, - *right; Oid opno, leftOp, rightOp; if (!is_opclause(clause)) return; - - left = get_leftop(clause); - right = get_rightop(clause); - - /* caution: is_opclause accepts more than I do, so check it */ - if (!right) - return; /* unary opclauses need not apply */ - if (!IsA(left, Var) || - !IsA(right, Var)) + if (length(((OpExpr *) clause)->args) != 2) return; opno = ((OpExpr *) clause)->opno; if (op_mergejoinable(opno, - left->vartype, - right->vartype, &leftOp, - &rightOp)) + &rightOp) && + !contain_volatile_functions((Node *) clause)) { restrictinfo->mergejoinoperator = opno; restrictinfo->left_sortop = leftOp; @@ -1002,34 +904,23 @@ check_mergejoinable(RestrictInfo *restrictinfo) * info fields in the restrictinfo. * * Currently, we support hashjoin for binary opclauses where - * both operands are simple Vars and the operator is a hashjoinable - * operator. + * the operator is a hashjoinable operator. The arguments can be + * anything --- as long as there are no volatile functions in them. */ static void check_hashjoinable(RestrictInfo *restrictinfo) { Expr *clause = restrictinfo->clause; - Var *left, - *right; Oid opno; if (!is_opclause(clause)) return; - - left = get_leftop(clause); - right = get_rightop(clause); - - /* caution: is_opclause accepts more than I do, so check it */ - if (!right) - return; /* unary opclauses need not apply */ - if (!IsA(left, Var) || - !IsA(right, Var)) + if (length(((OpExpr *) clause)->args) != 2) return; opno = ((OpExpr *) clause)->opno; - if (op_hashjoinable(opno, - left->vartype, - right->vartype)) + if (op_hashjoinable(opno) && + !contain_volatile_functions((Node *) clause)) restrictinfo->hashjoinoperator = opno; } diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 5d2634bf82d883d55d87da2bb7c6cebdc447c4d2..6e265931eb226edf60ac26d06f93019fa32ed1aa 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.72 2002/11/21 00:42:19 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.73 2003/01/15 19:35:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -117,7 +117,7 @@ query_planner(Query *root, List *tlist, double tuple_fraction, /* * Construct RelOptInfo nodes for all base relations in query. */ - (void) add_base_rels_to_query(root, (Node *) root->jointree); + add_base_rels_to_query(root, (Node *) root->jointree); /* * Examine the targetlist and qualifications, adding entries to diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 76a16cd833d64cf7ac1198b4e4861c5a1f747120..4cf6a09acfecbcc1b117195d871ba66aaffaf187 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.138 2003/01/13 18:10:53 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.139 2003/01/15 19:35:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -559,20 +559,6 @@ is_simple_subquery(Query *subquery) if (expression_returns_set((Node *) subquery->targetList)) return false; - /* - * Don't pull up a subquery that has any sublinks in its targetlist, - * either. As of PG 7.3 this creates problems because the pulled-up - * expressions may go into join alias lists, and the sublinks would - * not get fixed because we do flatten_join_alias_vars() too late. - * Eventually we should do a complete flatten_join_alias_vars as the - * first step of preprocess_expression, and then we could probably - * support this. (BUT: it might be a bad idea anyway, due to possibly - * causing multiple evaluations of an expensive sublink.) - */ - if (subquery->hasSubLinks && - contain_subplans((Node *) subquery->targetList)) - return false; - /* * Hack: don't try to pull up a subquery with an empty jointree. * query_planner() will correctly generate a Result plan for a @@ -750,6 +736,14 @@ preprocess_jointree(Query *parse, Node *jtnode) static Node * preprocess_expression(Query *parse, Node *expr, int kind) { + /* + * If the query has any join RTEs, replace join alias variables with + * base-relation variables. We must do this before sublink processing, + * else sublinks expanded out from join aliases wouldn't get processed. + */ + if (parse->hasJoinRTEs) + expr = flatten_join_alias_vars(expr, parse->rtable); + /* * Simplify constant expressions. * @@ -783,15 +777,6 @@ preprocess_expression(Query *parse, Node *expr, int kind) if (PlannerQueryLevel > 1) expr = SS_replace_correlation_vars(expr); - /* - * If the query has any join RTEs, try to replace join alias variables - * with base-relation variables, to allow quals to be pushed down. We - * must do this after sublink processing, since it does not recurse - * into sublinks. - */ - if (parse->hasJoinRTEs) - expr = flatten_join_alias_vars(expr, parse->rtable, false); - return expr; } diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 1b5c72e163c43e78d757944ab015a899aa514277..1c9f8a27cff9044b7eac8887ecd81f967f5626e9 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.88 2003/01/13 18:10:53 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.89 2003/01/15 19:35:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -278,8 +278,7 @@ fix_expr_references_walker(Node *node, void *context) * Note: this same transformation has already been applied to the quals * of the join by createplan.c. It's a little odd to do it here for the * targetlist and there for the quals, but it's easier that way. (Look - * at switch_outer() and the handling of nestloop inner indexscans to - * see why.) + * at the handling of nestloop inner indexscans to see why.) * * Because the quals are reference-adjusted sooner, we cannot do equal() * comparisons between qual and tlist var nodes during the time between @@ -379,8 +378,7 @@ set_uppernode_references(Plan *plan, Index subvarno) * Creates a new set of targetlist entries or join qual clauses by * changing the varno/varattno values of variables in the clauses * to reference target list values from the outer and inner join - * relation target lists. Also, any join alias variables in the - * clauses are expanded into references to their component variables. + * relation target lists. * * This is used in two different scenarios: a normal join clause, where * all the Vars in the clause *must* be replaced by OUTER or INNER references; @@ -428,7 +426,6 @@ join_references_mutator(Node *node, { Var *var = (Var *) node; Resdom *resdom; - Node *newnode; /* First look for the var in the input tlists */ resdom = tlist_member((Node *) var, context->outer_tlist); @@ -454,20 +451,6 @@ join_references_mutator(Node *node, if (var->varno == context->acceptable_rel) return (Node *) copyObject(var); - /* - * Perhaps it's a join alias that can be resolved to input vars? - * We try this last since it's relatively slow. - */ - newnode = flatten_join_alias_vars((Node *) var, - context->rtable, - true); - if (!equal(newnode, (Node *) var)) - { - /* Must now resolve the input vars... */ - newnode = join_references_mutator(newnode, context); - return newnode; - } - /* No referent found for Var */ elog(ERROR, "join_references: variable not in subplan target lists"); } diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 5177e210d3d66cfde830e1dd2c7f99507f81bbd8..4af395b860c2b43d0cbca0946d6f6f71f0e2d1d8 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.85 2003/01/12 22:35:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.86 2003/01/15 19:35:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -837,9 +837,7 @@ adjust_inherited_attrs_mutator(Node *node, } /* - * We have to process RestrictInfo nodes specially: we do NOT want to - * copy the original subclauseindices list, since the new rel may have - * different indices. The list will be rebuilt during later planning. + * We have to process RestrictInfo nodes specially. */ if (IsA(node, RestrictInfo)) { @@ -849,10 +847,41 @@ adjust_inherited_attrs_mutator(Node *node, /* Copy all flat-copiable fields */ memcpy(newinfo, oldinfo, sizeof(RestrictInfo)); + /* Recursively fix the clause itself */ newinfo->clause = (Expr *) adjust_inherited_attrs_mutator((Node *) oldinfo->clause, context); + /* + * We do NOT want to copy the original subclauseindices list, since + * the new rel will have different indices. The list will be rebuilt + * when needed during later planning. + */ newinfo->subclauseindices = NIL; + + /* + * Adjust left/right relids lists too. + */ + if (intMember(context->old_rt_index, oldinfo->left_relids)) + { + newinfo->left_relids = listCopy(oldinfo->left_relids); + newinfo->left_relids = lremovei(context->old_rt_index, + newinfo->left_relids); + newinfo->left_relids = lconsi(context->new_rt_index, + newinfo->left_relids); + } + else + newinfo->left_relids = oldinfo->left_relids; + if (intMember(context->old_rt_index, oldinfo->right_relids)) + { + newinfo->right_relids = listCopy(oldinfo->right_relids); + newinfo->right_relids = lremovei(context->old_rt_index, + newinfo->right_relids); + newinfo->right_relids = lconsi(context->new_rt_index, + newinfo->right_relids); + } + else + newinfo->right_relids = oldinfo->right_relids; + newinfo->eval_cost.startup = -1; /* reset these too */ newinfo->this_selec = -1; newinfo->left_pathkey = NIL; /* and these */ @@ -869,6 +898,7 @@ adjust_inherited_attrs_mutator(Node *node, * NOTE: we do not need to recurse into sublinks, because they should * already have been converted to subplans before we see them. */ + Assert(!IsA(node, SubLink)); /* * BUT: although we don't need to recurse into subplans, we do need to diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index e38fc46821d5eab93fdf18f5fe8eede2e77b1d75..233a8cb8947d45dd0384c756703f89d75942f72d 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.121 2003/01/10 21:08:13 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.122 2003/01/15 19:35:44 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -112,17 +112,13 @@ make_opclause(Oid opno, Oid opresulttype, bool opretset, * * Returns the left operand of a clause of the form (op expr expr) * or (op expr) - * - * NB: for historical reasons, the result is declared Var *, even - * though many callers can cope with results that are not Vars. - * The result really ought to be declared Expr * or Node *. */ -Var * +Node * get_leftop(Expr *clause) { OpExpr *expr = (OpExpr *) clause; - if (expr->args != NULL) + if (expr->args != NIL) return lfirst(expr->args); else return NULL; @@ -134,12 +130,12 @@ get_leftop(Expr *clause) * Returns the right operand in a clause of the form (op expr expr). * NB: result will be NULL if applied to a unary op clause. */ -Var * +Node * get_rightop(Expr *clause) { OpExpr *expr = (OpExpr *) clause; - if (expr->args != NULL && lnext(expr->args) != NULL) + if (expr->args != NIL && lnext(expr->args) != NIL) return lfirst(lnext(expr->args)); else return NULL; diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 296d211ad12166e58dc3f8d98a13e1473c837cd7..87207f617cc508eb2a46bf3365d2dc8a36d714ae 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.42 2003/01/12 22:35:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.43 2003/01/15 19:35:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -110,9 +110,9 @@ build_other_rel(Query *root, int relid) /* No existing RelOptInfo for this other rel, so make a new one */ rel = make_base_rel(root, relid); - /* if it's not a join rel, must be a child rel */ - if (rel->reloptkind == RELOPT_BASEREL) - rel->reloptkind = RELOPT_OTHER_CHILD_REL; + /* presently, must be an inheritance child rel */ + Assert(rel->reloptkind == RELOPT_BASEREL); + rel->reloptkind = RELOPT_OTHER_CHILD_REL; /* and add it to the list */ root->other_rel_list = lcons(rel, root->other_rel_list); @@ -146,8 +146,6 @@ make_base_rel(Query *root, int relid) rel->pages = 0; rel->tuples = 0; rel->subplan = NULL; - rel->joinrti = 0; - rel->joinrteids = NIL; rel->baserestrictinfo = NIL; rel->baserestrictcost.startup = 0; rel->baserestrictcost.per_tuple = 0; @@ -174,10 +172,6 @@ make_base_rel(Query *root, int relid) case RTE_FUNCTION: /* Subquery or function --- nothing to do here */ break; - case RTE_JOIN: - /* Join --- must be an otherrel */ - rel->reloptkind = RELOPT_OTHER_JOIN_REL; - break; default: elog(ERROR, "make_base_rel: unsupported RTE kind %d", (int) rte->rtekind); @@ -220,47 +214,6 @@ find_base_rel(Query *root, int relid) return NULL; /* keep compiler quiet */ } -/* - * find_other_rel - * Find an otherrel entry, if one exists for the given relid. - * Return NULL if no entry. - */ -RelOptInfo * -find_other_rel(Query *root, int relid) -{ - List *rels; - - foreach(rels, root->other_rel_list) - { - RelOptInfo *rel = (RelOptInfo *) lfirst(rels); - - if (lfirsti(rel->relids) == relid) - return rel; - } - return NULL; -} - -/* - * find_other_rel_for_join - * Look for an otherrel for a join RTE matching the given baserel set. - * Return NULL if no entry. - */ -RelOptInfo * -find_other_rel_for_join(Query *root, List *relids) -{ - List *rels; - - foreach(rels, root->other_rel_list) - { - RelOptInfo *rel = (RelOptInfo *) lfirst(rels); - - if (rel->reloptkind == RELOPT_OTHER_JOIN_REL - && sameseti(relids, rel->outerjoinset)) - return rel; - } - return NULL; -} - /* * find_join_rel * Returns relation entry corresponding to 'relids' (a list of RT indexes), @@ -310,7 +263,6 @@ build_join_rel(Query *root, { List *joinrelids; RelOptInfo *joinrel; - RelOptInfo *joinrterel; List *restrictlist; List *new_outer_tlist; List *new_inner_tlist; @@ -360,9 +312,6 @@ build_join_rel(Query *root, joinrel->pages = 0; joinrel->tuples = 0; joinrel->subplan = NULL; - joinrel->joinrti = 0; - joinrel->joinrteids = nconc(listCopy(outer_rel->joinrteids), - inner_rel->joinrteids); joinrel->baserestrictinfo = NIL; joinrel->baserestrictcost.startup = 0; joinrel->baserestrictcost.per_tuple = 0; @@ -371,15 +320,6 @@ build_join_rel(Query *root, joinrel->index_outer_relids = NIL; joinrel->index_inner_paths = NIL; - /* Is there a join RTE matching this join? */ - joinrterel = find_other_rel_for_join(root, joinrelids); - if (joinrterel) - { - /* Yes, remember its RT index */ - joinrel->joinrti = lfirsti(joinrterel->relids); - joinrel->joinrteids = lconsi(joinrel->joinrti, joinrel->joinrteids); - } - /* * Create a new tlist by removing irrelevant elements from both tlists * of the outer and inner join relations and then merging the results @@ -400,23 +340,6 @@ build_join_rel(Query *root, length(new_outer_tlist) + 1); joinrel->targetlist = nconc(new_outer_tlist, new_inner_tlist); - /* - * If there are any alias variables attached to the matching join RTE, - * attach them to the tlist too, so that they will be evaluated for - * use at higher plan levels. - */ - if (joinrterel) - { - List *jrtetl; - - foreach(jrtetl, joinrterel->targetlist) - { - TargetEntry *jrtete = lfirst(jrtetl); - - add_var_to_tlist(joinrel, (Var *) jrtete->expr); - } - } - /* * Construct restrict and join clause lists for the new joinrel. (The * caller might or might not need the restrictlist, but I need it diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index 92ff0cd5b4c003b540af9d0c16e0c9d00489be67..11267428d7ae8a9936091e6e48d2e441c04e4686 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.43 2003/01/10 21:08:13 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.44 2003/01/15 19:35:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,16 @@ #include "parser/parsetree.h" +/* macros borrowed from expression_tree_mutator */ + +#define FLATCOPY(newnode, node, nodetype) \ + ( (newnode) = makeNode(nodetype), \ + memcpy((newnode), (node), sizeof(nodetype)) ) + +#define MUTATE(newfield, oldfield, fieldtype, mutator, context) \ + ( (newfield) = (fieldtype) mutator((Node *) (oldfield), (context)) ) + + typedef struct { List *varlist; @@ -42,7 +52,7 @@ typedef struct typedef struct { List *rtable; - bool force; + int sublevels_up; } flatten_join_alias_vars_context; static bool pull_varnos_walker(Node *node, @@ -314,26 +324,16 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context) * relation variables instead. This allows quals involving such vars to be * pushed down. * - * If force is TRUE then we will reduce all JOIN alias Vars to non-alias Vars - * or expressions thereof (there may be COALESCE and/or type conversions - * involved). If force is FALSE we will not expand a Var to a non-Var - * expression. This is a hack to avoid confusing mergejoin planning, which - * currently cannot cope with non-Var join items --- we leave the join vars - * as Vars till after planning is done, then expand them during setrefs.c. - * - * Upper-level vars (with varlevelsup > 0) are ignored; normally there - * should not be any by the time this routine is called. - * - * Does not examine subqueries, therefore must only be used after reduction - * of sublinks to subplans! + * NOTE: this is used on not-yet-planned expressions. We do not expect it + * to be applied directly to a Query node. */ Node * -flatten_join_alias_vars(Node *node, List *rtable, bool force) +flatten_join_alias_vars(Node *node, List *rtable) { flatten_join_alias_vars_context context; context.rtable = rtable; - context.force = force; + context.sublevels_up = 0; return flatten_join_alias_vars_mutator(node, &context); } @@ -350,21 +350,31 @@ flatten_join_alias_vars_mutator(Node *node, RangeTblEntry *rte; Node *newvar; - if (var->varlevelsup != 0) + if (var->varlevelsup != context->sublevels_up) return node; /* no need to copy, really */ rte = rt_fetch(var->varno, context->rtable); if (rte->rtekind != RTE_JOIN) return node; Assert(var->varattno > 0); newvar = (Node *) nth(var->varattno - 1, rte->joinaliasvars); - if (IsA(newvar, Var) ||context->force) - { - /* expand it; recurse in case join input is itself a join */ - return flatten_join_alias_vars_mutator(newvar, context); - } - /* we don't want to force expansion of this alias Var */ - return node; + /* expand it; recurse in case join input is itself a join */ + return flatten_join_alias_vars_mutator(newvar, context); + } + if (IsA(node, Query)) + { + /* Recurse into RTE subquery or not-yet-planned sublink subquery */ + Query *query = (Query *) node; + Query *newnode; + + FLATCOPY(newnode, query, Query); + context->sublevels_up++; + query_tree_mutator(newnode, flatten_join_alias_vars_mutator, + (void *) context, QTW_IGNORE_JOINALIASES); + context->sublevels_up--; + return (Node *) newnode; } + /* Already-planned tree not supported */ + Assert(!is_subplan(node)); return expression_tree_mutator(node, flatten_join_alias_vars_mutator, (void *) context); } diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 977cbe0a5e48a7c4d43f2e401e1f3c3583c0d281..fe6f38eee8509e5f7a7cb9723dba6364570f3353 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.125 2003/01/12 22:35:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.126 2003/01/15 19:35:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1754,8 +1754,8 @@ mergejoinscansel(Query *root, Node *clause, if (!is_opclause(clause)) return; /* shouldn't happen */ opno = ((OpExpr *) clause)->opno; - left = get_leftop((Expr *) clause); - right = get_rightop((Expr *) clause); + left = (Var *) get_leftop((Expr *) clause); + right = (Var *) get_rightop((Expr *) clause); if (!right) return; /* shouldn't happen */ @@ -1766,8 +1766,6 @@ mergejoinscansel(Query *root, Node *clause, /* Verify mergejoinability and get left and right "<" operators */ if (!op_mergejoinable(opno, - left->vartype, - right->vartype, &lsortop, &rsortop)) return; /* shouldn't happen */ @@ -1892,17 +1890,6 @@ estimate_num_groups(Query *root, List *groupClauses, double input_rows) List *varshere; varshere = pull_var_clause(groupexpr, false); - /* - * Replace any JOIN alias Vars with the underlying Vars. (This - * is not really right for FULL JOIN ...) - */ - if (root->hasJoinRTEs) - { - varshere = (List *) flatten_join_alias_vars((Node *) varshere, - root->rtable, - true); - varshere = pull_var_clause((Node *) varshere, false); - } /* * If we find any variable-free GROUP BY item, then either it is * a constant (and we can ignore it) or it contains a volatile @@ -1963,7 +1950,7 @@ estimate_num_groups(Query *root, List *groupClauses, double input_rows) l2 = lnext(l2); if (var->varno != varinfo->var->varno && - vars_known_equal(root, var, varinfo->var)) + exprs_known_equal(root, (Node *) var, (Node *) varinfo->var)) { /* Found a match */ if (varinfo->ndistinct <= ndistinct) diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 56dabcd754284f633f72437bcbca4889d4c0a8e7..a6c838974c0d30562d5853c95f9edfdf414a24d7 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.88 2002/12/05 04:04:44 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.89 2003/01/15 19:35:44 tgl Exp $ * * NOTES * Eventually, the index information should go through here, too. @@ -315,11 +315,11 @@ get_opname(Oid opno) /* * op_mergejoinable * - * Returns the left and right sort operators and types corresponding to a - * mergejoinable operator, or nil if the operator is not mergejoinable. + * Returns the left and right sort operators corresponding to a + * mergejoinable operator, or false if the operator is not mergejoinable. */ bool -op_mergejoinable(Oid opno, Oid ltype, Oid rtype, Oid *leftOp, Oid *rightOp) +op_mergejoinable(Oid opno, Oid *leftOp, Oid *rightOp) { HeapTuple tp; bool result = false; @@ -332,9 +332,7 @@ op_mergejoinable(Oid opno, Oid ltype, Oid rtype, Oid *leftOp, Oid *rightOp) Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); if (optup->oprlsortop && - optup->oprrsortop && - optup->oprleft == ltype && - optup->oprright == rtype) + optup->oprrsortop) { *leftOp = optup->oprlsortop; *rightOp = optup->oprrsortop; @@ -391,14 +389,13 @@ op_mergejoin_crossops(Oid opno, Oid *ltop, Oid *gtop, /* * op_hashjoinable * - * Returns the hash operator corresponding to a hashjoinable operator, - * or InvalidOid if the operator is not hashjoinable. + * Returns true if the operator is hashjoinable. */ -Oid -op_hashjoinable(Oid opno, Oid ltype, Oid rtype) +bool +op_hashjoinable(Oid opno) { HeapTuple tp; - Oid result = InvalidOid; + bool result = false; tp = SearchSysCache(OPEROID, ObjectIdGetDatum(opno), @@ -407,10 +404,7 @@ op_hashjoinable(Oid opno, Oid ltype, Oid rtype) { Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp); - if (optup->oprcanhash && - optup->oprleft == ltype && - optup->oprright == rtype) - result = opno; + result = optup->oprcanhash; ReleaseSysCache(tp); } return result; diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index d3fb2231f1e99be2a4201207106158a6767d9555..a21debe02f90c81d1a9f9f87e50f52d16658622f 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: relation.h,v 1.75 2003/01/12 22:35:29 tgl Exp $ + * $Id: relation.h,v 1.76 2003/01/15 19:35:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -50,7 +50,7 @@ typedef struct QualCost * Per-relation information for planning/optimization * * For planning purposes, a "base rel" is either a plain relation (a table) - * or the output of a sub-SELECT that appears in the range table. + * or the output of a sub-SELECT or function that appears in the range table. * In either case it is uniquely identified by an RT index. A "joinrel" * is the joining of two or more base rels. A joinrel is identified by * the set of RT indexes for its component baserels. We create RelOptInfo @@ -63,14 +63,10 @@ typedef struct QualCost * * We also have "other rels", which are like base rels in that they refer to * single RT indexes; but they are not part of the join tree, and are stored - * in other_rel_list not base_rel_list. An otherrel is created for each - * join RTE as an aid in processing Vars that refer to the join's outputs, - * but it serves no other purpose in planning. It is important not to - * confuse this otherrel with the joinrel that represents the matching set - * of base relations. - * - * A second category of otherrels are those made for child relations of an - * inheritance scan (SELECT FROM foo*). The parent table's RTE and + * in other_rel_list not base_rel_list. + * + * Currently the only kind of otherrels are those made for child relations + * of an inheritance scan (SELECT FROM foo*). The parent table's RTE and * corresponding baserel represent the whole result of the inheritance scan. * The planner creates separate RTEs and associated RelOptInfos for each child * table (including the parent table, in its capacity as a member of the @@ -80,6 +76,10 @@ typedef struct QualCost * the inheritance set; then the parent baserel is given an Append plan * comprising the best plans for the individual child tables. * + * At one time we also made otherrels to represent join RTEs, for use in + * handling join alias Vars. Currently this is not needed because all join + * alias Vars are expanded to non-aliased form during preprocess_expression. + * * Parts of this data structure are specific to various scan and join * mechanisms. It didn't seem worth creating new node types for them. * @@ -114,15 +114,7 @@ typedef struct QualCost * set_base_rel_pathlist processes the object. * * For otherrels that are inheritance children, these fields are filled - * in just as for a baserel. In otherrels for join RTEs, these fields - * are empty --- the only useful field of a join otherrel is its - * outerjoinset. - * - * If the relation is a join relation it will have these fields set: - * - * joinrti - RT index of corresponding JOIN RTE, if any; 0 if none - * joinrteids - List of RT indexes of JOIN RTEs included in this join - * (including joinrti) + * in just as for a baserel. * * The presence of the remaining fields depends on the restrictions * and joins that the relation participates in: @@ -135,8 +127,7 @@ typedef struct QualCost * outerjoinset - For a base rel: if the rel appears within the nullable * side of an outer join, the list of all relids * participating in the highest such outer join; else NIL. - * For a join otherrel: the list of all baserel relids - * syntactically within the join. Otherwise, unused. + * Otherwise, unused. * joininfo - List of JoinInfo nodes, containing info about each join * clause in which this relation participates * index_outer_relids - only used for base rels; list of outer relids @@ -170,7 +161,6 @@ typedef enum RelOptKind { RELOPT_BASEREL, RELOPT_JOINREL, - RELOPT_OTHER_JOIN_REL, RELOPT_OTHER_CHILD_REL } RelOptKind; @@ -202,10 +192,6 @@ typedef struct RelOptInfo double tuples; struct Plan *subplan; /* if subquery */ - /* information about a join rel (not set for base rels!) */ - Index joinrti; - List *joinrteids; - /* used by various scans and joins: */ List *baserestrictinfo; /* RestrictInfo structures (if * base rel) */ @@ -275,15 +261,6 @@ typedef struct IndexOptInfo } IndexOptInfo; -/* - * A Var is considered to belong to a relation if it's either from one - * of the actual base rels making up the relation, or it's a join alias - * var that is included in the relation. - */ -#define VARISRELMEMBER(varno,rel) (intMember((varno), (rel)->relids) || \ - intMember((varno), (rel)->joinrteids)) - - /* * PathKeys * @@ -583,6 +560,15 @@ typedef struct RestrictInfo QualCost eval_cost; /* eval cost of clause; -1 if not yet set */ Selectivity this_selec; /* selectivity; -1 if not yet set */ + /* + * If the clause looks useful for joining --- that is, it is a binary + * opclause with nonoverlapping sets of relids referenced in the left + * and right sides --- then these two fields are set to lists of the + * referenced relids. Otherwise they are both NIL. + */ + List *left_relids; /* relids in left side of join clause */ + List *right_relids; /* relids in right side of join clause */ + /* valid if clause is mergejoinable, else InvalidOid: */ Oid mergejoinoperator; /* copy of clause operator */ Oid left_sortop; /* leftside sortop needed for mergejoin */ diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index f280e420d9b4c85a44123aad48df1398b503df9b..4ed022b15e766c4de1006cd32b7f43b639bbb47a 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: clauses.h,v 1.58 2002/12/14 00:17:59 tgl Exp $ + * $Id: clauses.h,v 1.59 2003/01/15 19:35:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -25,8 +25,8 @@ extern Expr *make_opclause(Oid opno, Oid opresulttype, bool opretset, Expr *leftop, Expr *rightop); -extern Var *get_leftop(Expr *clause); -extern Var *get_rightop(Expr *clause); +extern Node *get_leftop(Expr *clause); +extern Node *get_rightop(Expr *clause); extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset, CoercionForm funcformat, List *funcargs); diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 65abeb0cf61694abacf6fbbd778341b455b86b79..77ed27e7e55b23de9a2acc3c87567af330b03edd 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pathnode.h,v 1.46 2002/11/30 05:21:03 tgl Exp $ + * $Id: pathnode.h,v 1.47 2003/01/15 19:35:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -74,8 +74,6 @@ extern HashPath *create_hashjoin_path(Query *root, extern void build_base_rel(Query *root, int relid); extern RelOptInfo *build_other_rel(Query *root, int relid); extern RelOptInfo *find_base_rel(Query *root, int relid); -extern RelOptInfo *find_other_rel(Query *root, int relid); -extern RelOptInfo *find_other_rel_for_join(Query *root, List *relids); extern RelOptInfo *build_join_rel(Query *root, RelOptInfo *outer_rel, RelOptInfo *inner_rel, diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 134adad9b604c99f35225bf6042a7cebd9036f78..f2604527001e039ffb1048e293b2fde2d3bfa876 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: planmain.h,v 1.64 2002/12/12 15:49:41 tgl Exp $ + * $Id: planmain.h,v 1.65 2003/01/15 19:35:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,8 +32,6 @@ extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual, extern Append *make_append(List *appendplans, bool isTarget, List *tlist); extern Sort *make_sort(Query *root, List *tlist, Plan *lefttree, int keycount); -extern Sort *make_sort_from_pathkeys(Query *root, List *tlist, - Plan *lefttree, List *pathkeys); extern Agg *make_agg(Query *root, List *tlist, List *qual, AggStrategy aggstrategy, int numGroupCols, AttrNumber *grpColIdx, @@ -54,12 +52,12 @@ extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan); /* * prototypes for plan/initsplan.c */ -extern List *add_base_rels_to_query(Query *root, Node *jtnode); +extern void add_base_rels_to_query(Query *root, Node *jtnode); extern void build_base_rel_tlists(Query *root, List *tlist); extern Relids distribute_quals_to_rels(Query *root, Node *jtnode); extern void process_implied_equality(Query *root, Node *item1, Node *item2, Oid sortop1, Oid sortop2); -extern bool vars_known_equal(Query *root, Var *var1, Var *var2); +extern bool exprs_known_equal(Query *root, Node *item1, Node *item2); /* * prototypes for plan/setrefs.c diff --git a/src/include/optimizer/var.h b/src/include/optimizer/var.h index 102e928a544b87e99595f78d010d9efeaebb4c62..07b8b311d076ae4f43d79cec6ea28bd2d1d44d92 100644 --- a/src/include/optimizer/var.h +++ b/src/include/optimizer/var.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: var.h,v 1.23 2002/12/12 20:35:16 tgl Exp $ + * $Id: var.h,v 1.24 2003/01/15 19:35:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -23,6 +23,6 @@ extern bool contain_var_reference(Node *node, int varno, int varattno, extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup); extern bool contain_var_clause(Node *node); extern List *pull_var_clause(Node *node, bool includeUpperVars); -extern Node *flatten_join_alias_vars(Node *node, List *rtable, bool force); +extern Node *flatten_join_alias_vars(Node *node, List *rtable); #endif /* VAR_H */ diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index a0d3574b246e61266efa4bfab102671859f4fe61..8200730f6a25676031c670d7710f2a546022bf5d 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: lsyscache.h,v 1.65 2002/12/01 21:05:14 tgl Exp $ + * $Id: lsyscache.h,v 1.66 2003/01/15 19:35:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -26,11 +26,10 @@ extern void get_atttypetypmod(Oid relid, AttrNumber attnum, extern bool opclass_is_btree(Oid opclass); 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 bool op_mergejoinable(Oid opno, 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_hashjoinable(Oid opno); extern bool op_strict(Oid opno); extern char op_volatile(Oid opno); extern Oid get_commutator(Oid opno); diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 3decbdb7991dfa01557f019eb5cbb413d80adc46..7ef807a95db1375103f19a0f1d55ad210dc8c48c 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -498,6 +498,17 @@ WHERE p1.oprcode = p2.oid AND -----+---------+-----+--------- (0 rows) +-- If the operator is mergejoinable or hashjoinable, its underlying function +-- should not be volatile. +SELECT p1.oid, p1.oprname, p2.oid, p2.proname +FROM pg_operator AS p1, pg_proc AS p2 +WHERE p1.oprcode = p2.oid AND + (p1.oprlsortop != 0 OR p1.oprcanhash) AND + p2.provolatile = 'v'; + oid | oprname | oid | proname +-----+---------+-----+--------- +(0 rows) + -- If oprrest is set, the operator must return boolean, -- and it must link to a proc with the right signature -- to be a restriction selectivity estimator. @@ -583,7 +594,8 @@ WHERE a.aggfnoid = p.oid AND a.aggtranstype != p2.prorettype OR a.aggtranstype != p2.proargtypes[0] OR NOT ((p2.pronargs = 2 AND p.proargtypes[0] = p2.proargtypes[1]) OR - (p2.pronargs = 1 AND p.proargtypes[0] = '"any"'::regtype))); + (p2.pronargs = 1 AND p.proargtypes[0] = '"any"'::regtype))) +ORDER BY 1; aggfnoid | proname | oid | proname ----------+---------+-----+------------- 2121 | max | 768 | int4larger diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 46c153e5d14042ed1722548f7f57c68939815521..8f943188d24e87cc3e3eab4e9228e66e5beaddc0 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -107,7 +107,8 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT (p1.typelem != 0 AND p1.typlen < 0) AND NOT - (p2.prorettype = p1.oid AND NOT p2.proretset); + (p2.prorettype = p1.oid AND NOT p2.proretset) +ORDER BY 1; oid | typname | oid | proname ------+-----------+-----+----------- 32 | SET | 109 | unknownin @@ -132,7 +133,8 @@ FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT ((p2.pronargs = 1 AND p2.proargtypes[0] = p1.oid) OR (p2.oid = 'array_out'::regproc AND - p1.typelem != 0 AND p1.typlen = -1)); + p1.typelem != 0 AND p1.typlen = -1)) +ORDER BY 1; oid | typname | oid | proname ------+-----------+-----+------------ 32 | SET | 110 | unknownout diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql index faacc83850e8c7f673e054387d3ea811c2abab28..650073cccc1dd128cd6930dd50d4b51261ad9a2d 100644 --- a/src/test/regress/sql/opr_sanity.sql +++ b/src/test/regress/sql/opr_sanity.sql @@ -416,6 +416,15 @@ WHERE p1.oprcode = p2.oid AND (p1.oprleft != p2.proargtypes[0] AND p2.proargtypes[0] != 0) OR p1.oprright != 0); +-- If the operator is mergejoinable or hashjoinable, its underlying function +-- should not be volatile. + +SELECT p1.oid, p1.oprname, p2.oid, p2.proname +FROM pg_operator AS p1, pg_proc AS p2 +WHERE p1.oprcode = p2.oid AND + (p1.oprlsortop != 0 OR p1.oprcanhash) AND + p2.provolatile = 'v'; + -- If oprrest is set, the operator must return boolean, -- and it must link to a proc with the right signature -- to be a restriction selectivity estimator. @@ -490,7 +499,8 @@ WHERE a.aggfnoid = p.oid AND a.aggtranstype != p2.prorettype OR a.aggtranstype != p2.proargtypes[0] OR NOT ((p2.pronargs = 2 AND p.proargtypes[0] = p2.proargtypes[1]) OR - (p2.pronargs = 1 AND p.proargtypes[0] = '"any"'::regtype))); + (p2.pronargs = 1 AND p.proargtypes[0] = '"any"'::regtype))) +ORDER BY 1; -- Cross-check finalfn (if present) against its entry in pg_proc. -- FIXME: what about binary-compatible types? diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql index 0cc4748fa89b95b6363a493e00880529ee80019d..f0ffa5984dc374abb0e851bc0e2146950888b353 100644 --- a/src/test/regress/sql/type_sanity.sql +++ b/src/test/regress/sql/type_sanity.sql @@ -90,7 +90,8 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT (p1.typelem != 0 AND p1.typlen < 0) AND NOT - (p2.prorettype = p1.oid AND NOT p2.proretset); + (p2.prorettype = p1.oid AND NOT p2.proretset) +ORDER BY 1; -- Varlena array types will point to array_in SELECT p1.oid, p1.typname, p2.oid, p2.proname @@ -108,7 +109,8 @@ FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT ((p2.pronargs = 1 AND p2.proargtypes[0] = p1.oid) OR (p2.oid = 'array_out'::regproc AND - p1.typelem != 0 AND p1.typlen = -1)); + p1.typelem != 0 AND p1.typlen = -1)) +ORDER BY 1; SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2