Clean up handling of variable-free qual clauses. System now does the
authorTom Lane <[email protected]>
Sun, 13 Aug 2000 02:50:35 +0000 (02:50 +0000)
committerTom Lane <[email protected]>
Sun, 13 Aug 2000 02:50:35 +0000 (02:50 +0000)
right thing with variable-free clauses that contain noncachable functions,
such as 'WHERE random() < 0.5' --- these are evaluated once per
potential output tuple.  Expressions that contain only Params are
now candidates to be indexscan quals --- for example, 'var = ($1 + 1)'
can now be indexed.  Cope with RelabelType nodes atop potential indexscan
variables --- this oversight prevents 7.0.* from recognizing some
potentially indexscanable situations.

15 files changed:
src/backend/executor/execProcnode.c
src/backend/executor/nodeIndexscan.c
src/backend/executor/nodeNestloop.c
src/backend/optimizer/path/clausesel.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/initsplan.c
src/backend/optimizer/plan/planmain.c
src/backend/optimizer/util/clauses.c
src/backend/utils/cache/lsyscache.c
src/include/executor/nodeNestloop.h
src/include/nodes/execnodes.h
src/include/optimizer/clauses.h
src/include/utils/lsyscache.h
src/pl/plpgsql/src/pl_exec.c

index 07b894843dac76d7f3ab5a7fc81c8c0e0a9a0d59..8c9970a6fa7b6069f858e4b1c9d07d250748bb8f 100644 (file)
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.18 2000/01/26 05:56:21 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.19 2000/08/13 02:50:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -277,7 +277,7 @@ ExecProcNode(Plan *node, Plan *parent)
                         * ----------------
                         */
                case T_NestLoop:
-                       result = ExecNestLoop((NestLoop *) node, parent);
+                       result = ExecNestLoop((NestLoop *) node);
                        break;
 
                case T_MergeJoin:
index 0eb3b9f5ae9d8377cceec53da686bfba6cb1fd87..57770d2405806ce5c06fadfc2bce8a95ccbce3bb 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.52 2000/07/12 02:37:03 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.53 2000/08/13 02:50:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -281,6 +281,16 @@ IndexNext(IndexScan *node)
 TupleTableSlot *
 ExecIndexScan(IndexScan *node)
 {
+       IndexScanState *indexstate = node->indxstate;
+
+       /* ----------------
+        *      If we have runtime keys and they've not already been set up,
+        *      do it now.
+        * ----------------
+        */
+       if (indexstate->iss_RuntimeKeyInfo && !indexstate->iss_RuntimeKeysReady)
+               ExecReScan((Plan *) node, NULL, NULL);
+
        /* ----------------
         *      use IndexNext as access method
         * ----------------
@@ -335,9 +345,10 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
        scanKeys = indexstate->iss_ScanKeys;
        runtimeKeyInfo = indexstate->iss_RuntimeKeyInfo;
        numScanKeys = indexstate->iss_NumScanKeys;
-       indexstate->iss_IndexPtr = -1;
        if (ScanDirectionIsBackward(node->indxorderdir))
                indexstate->iss_IndexPtr = numIndices;
+       else
+               indexstate->iss_IndexPtr = -1;
 
        if (econtext)
        {
@@ -420,6 +431,9 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
                skey = scanKeys[i];
                index_rescan(scan, direction, skey);
        }
+
+       if (runtimeKeyInfo)
+               indexstate->iss_RuntimeKeysReady = true;
 }
 
 /* ----------------------------------------------------------------
@@ -603,7 +617,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
        Relation        currentRelation;
        HeapScanDesc currentScanDesc;
        ScanDirection direction;
-       List       *execParam = NIL;
 
        /* ----------------
         *      assign execution state to node
@@ -656,6 +669,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
        indexstate->iss_NumScanKeys = NULL;
        indexstate->iss_RuntimeKeyInfo = NULL;
        indexstate->iss_RuntimeContext = NULL;
+       indexstate->iss_RuntimeKeysReady = false;
        indexstate->iss_RelationDescs = NULL;
        indexstate->iss_ScanDescs = NULL;
 
@@ -787,6 +801,9 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
                         */
                        leftop = (Node *) get_leftop(clause);
 
+                       if (leftop && IsA(leftop, RelabelType))
+                               leftop = ((RelabelType *) leftop)->arg;
+
                        Assert(leftop != NULL);
 
                        if (IsA(leftop, Var) && var_is_rel((Var *) leftop))
@@ -827,7 +844,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
                                        /* treat Param as runtime key */
                                        have_runtime_keys = true;
                                        run_keys[j] = LEFT_OP;
-                                       execParam = lappendi(execParam, ((Param *) leftop)->paramid);
                                }
                                else
                                {
@@ -857,6 +873,9 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
                         */
                        rightop = (Node *) get_rightop(clause);
 
+                       if (rightop && IsA(rightop, RelabelType))
+                               rightop = ((RelabelType *) rightop)->arg;
+
                        Assert(rightop != NULL);
 
                        if (IsA(rightop, Var) && var_is_rel((Var *) rightop))
@@ -906,7 +925,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
                                        /* treat Param as runtime key */
                                        have_runtime_keys = true;
                                        run_keys[j] = RIGHT_OP;
-                                       execParam = lappendi(execParam, ((Param *) rightop)->paramid);
                                }
                                else
                                {
@@ -1068,12 +1086,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
        indexstate->iss_RelationDescs = relationDescs;
        indexstate->iss_ScanDescs = scanDescs;
 
-       /*
-        * if there are some PARAM_EXEC in scankeys then force index rescan on
-        * first scan.
-        */
-       ((Plan *) node)->chgParam = execParam;
-
        /* ----------------
         *      all done.
         * ----------------
index 70b98a97e3a19bcdc79ac3acb8bd2f5f3d792075..f59c1b0f602d2d518abb15581308da85752a7946 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.18 2000/07/17 03:04:53 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.19 2000/08/13 02:50:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -57,7 +57,7 @@
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
-ExecNestLoop(NestLoop *node, Plan *parent)
+ExecNestLoop(NestLoop *node)
 {
        NestLoopState *nlstate;
        Plan       *innerPlan;
@@ -187,7 +187,7 @@ ExecNestLoop(NestLoop *node, Plan *parent)
                         * outer tuple (e.g. in index scans), that's why we pass our
                         * expr context.
                         */
-                       ExecReScan(innerPlan, econtext, parent);
+                       ExecReScan(innerPlan, econtext, (Plan *) node);
 
                        ENL1_printf("getting new inner tuple");
 
index ba2e7e67b32bd19862b3327affc2bfdbb4f15561..7d80e288199298fc2bd3fca3d4d73265ebb743ef 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.38 2000/06/08 22:37:09 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.39 2000/08/13 02:50:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -123,7 +123,7 @@ clauselist_selectivity(Query *root,
                Selectivity s2;
 
                /*
-                * See if it looks like a restriction clause with a Const or Param
+                * See if it looks like a restriction clause with a pseudoconstant
                 * on one side.  (Anything more complicated than that might not
                 * behave in the simple way we are expecting.)
                 *
@@ -146,7 +146,7 @@ clauselist_selectivity(Query *root,
 
                                other = (flag & SEL_RIGHT) ? get_rightop((Expr *) clause) :
                                        get_leftop((Expr *) clause);
-                               if (IsA(other, Const) || IsA(other, Param))
+                               if (is_pseudo_constant_clause((Node *) other))
                                {
                                        Oid             opno = ((Oper *) ((Expr *) clause)->oper)->opno;
                                        RegProcedure oprrest = get_oprrest(opno);
@@ -533,6 +533,13 @@ clause_selectivity(Query *root,
                 */
                s1 = 1.0;
        }
+       else if (IsA(clause, RelabelType))
+       {
+               /* Not sure this case is needed, but it can't hurt */
+               s1 = clause_selectivity(root,
+                                                               ((RelabelType *) clause)->arg,
+                                                               varRelid);
+       }
 
        return s1;
 }
index 13bf65e699040fc90ec98083694efafa1658cd4c..27ae4056d99124a82d478d4e0b4fa08c48d686b5 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.92 2000/08/08 15:41:30 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.93 2000/08/13 02:50:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -649,7 +649,7 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel,
  *       a key of an index.
  *
  *       To match, the clause:
-
+ *
  *       (1a) for a restriction clause: must be in the form (indexkey op const)
  *                or (const op indexkey), or
  *       (1b) for a join clause: must be in the form (indexkey op others)
@@ -708,11 +708,11 @@ match_clause_to_indexkey(RelOptInfo *rel,
                /*
                 * Not considering joins, so check for clauses of the form:
                 * (indexkey operator constant) or (constant operator indexkey).
-                * We will accept a Param as being constant.
+                * Anything that is a "pseudo constant" expression will do.
                 */
 
-               if ((IsA(rightop, Const) ||IsA(rightop, Param)) &&
-                       match_index_to_operand(indexkey, leftop, rel, index))
+               if (match_index_to_operand(indexkey, leftop, rel, index) &&
+                       is_pseudo_constant_clause((Node *) rightop))
                {
                        if (is_indexable_operator(clause, opclass, index->relam, true))
                                return true;
@@ -726,8 +726,8 @@ match_clause_to_indexkey(RelOptInfo *rel,
                                return true;
                        return false;
                }
-               if ((IsA(leftop, Const) ||IsA(leftop, Param)) &&
-                       match_index_to_operand(indexkey, rightop, rel, index))
+               if (match_index_to_operand(indexkey, rightop, rel, index) &&
+                       is_pseudo_constant_clause((Node *) leftop))
                {
                        if (is_indexable_operator(clause, opclass, index->relam, false))
                                return true;
@@ -748,29 +748,32 @@ match_clause_to_indexkey(RelOptInfo *rel,
                /*
                 * Check for an indexqual that could be handled by a nestloop
                 * join. We need the index key to be compared against an
-                * expression that uses none of the indexed relation's vars.
+                * expression that uses none of the indexed relation's vars
+                * and contains no non-cachable functions.
                 */
                if (match_index_to_operand(indexkey, leftop, rel, index))
                {
                        List       *othervarnos = pull_varnos((Node *) rightop);
                        bool            isIndexable;
 
-                       isIndexable = !intMember(lfirsti(rel->relids), othervarnos);
+                       isIndexable =
+                               !intMember(lfirsti(rel->relids), othervarnos) &&
+                               !contain_noncachable_functions((Node *) rightop) &&
+                               is_indexable_operator(clause, opclass, index->relam, true);
                        freeList(othervarnos);
-                       if (isIndexable &&
-                         is_indexable_operator(clause, opclass, index->relam, true))
-                               return true;
+                       return isIndexable;
                }
                else if (match_index_to_operand(indexkey, rightop, rel, index))
                {
                        List       *othervarnos = pull_varnos((Node *) leftop);
                        bool            isIndexable;
 
-                       isIndexable = !intMember(lfirsti(rel->relids), othervarnos);
+                       isIndexable =
+                               !intMember(lfirsti(rel->relids), othervarnos) &&
+                               !contain_noncachable_functions((Node *) leftop) &&
+                               is_indexable_operator(clause, opclass, index->relam, false);
                        freeList(othervarnos);
-                       if (isIndexable &&
-                        is_indexable_operator(clause, opclass, index->relam, false))
-                               return true;
+                       return isIndexable;
                }
        }
 
@@ -790,7 +793,9 @@ match_clause_to_indexkey(RelOptInfo *rel,
  * recognizing binary-compatible datatypes.  For example, if we have
  * an expression like "oid = 123", the operator will be oideqint4,
  * which we need to replace with oideq in order to recognize it as
- * matching an oid_ops index on the oid field.
+ * matching an oid_ops index on the oid field.  A variant case is where
+ * the expression is like "oid::int4 = 123", where the given operator
+ * will be int4eq and again we need to intuit that we want to use oideq.
  *
  * Returns the OID of the matching operator, or InvalidOid if no match.
  * Note that the returned OID will be different from the one in the given
@@ -804,8 +809,13 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
 {
        Oid                     expr_op = ((Oper *) clause->oper)->opno;
        Oid                     commuted_op;
+       Operator        oldop,
+                               newop;
+       Form_pg_operator oldopform;
+       char       *opname;
        Oid                     ltype,
-                               rtype;
+                               rtype,
+                               indexkeytype;
 
        /* Get the commuted operator if necessary */
        if (indexkey_on_left)
@@ -821,48 +831,72 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
 
        /*
         * Maybe the index uses a binary-compatible operator set.
+        *
+        * Get the nominal input types of the given operator and the actual
+        * type (before binary-compatible relabeling) of the index key.
         */
-       ltype = exprType((Node *) get_leftop(clause));
-       rtype = exprType((Node *) get_rightop(clause));
+       oldop = get_operator_tuple(expr_op);
+       if (! HeapTupleIsValid(oldop))
+               return InvalidOid;              /* probably can't happen */
+       oldopform = (Form_pg_operator) GETSTRUCT(oldop);
+       opname = NameStr(oldopform->oprname);
+       ltype = oldopform->oprleft;
+       rtype = oldopform->oprright;
+
+       if (indexkey_on_left)
+       {
+               Node   *leftop = (Node *) get_leftop(clause);
+
+               if (leftop && IsA(leftop, RelabelType))
+                       leftop = ((RelabelType *) leftop)->arg;
+               indexkeytype = exprType(leftop);
+       }
+       else
+       {
+               Node   *rightop = (Node *) get_rightop(clause);
+
+               if (rightop && IsA(rightop, RelabelType))
+                       rightop = ((RelabelType *) rightop)->arg;
+               indexkeytype = exprType(rightop);
+       }
 
        /*
-        * make sure we have two different binary-compatible types...
+        * Make sure we have different but binary-compatible types.
         */
-       if (ltype != rtype && IS_BINARY_COMPATIBLE(ltype, rtype))
-       {
-               char       *opname = get_opname(expr_op);
-               Operator        newop;
+       if (ltype == indexkeytype && rtype == indexkeytype)
+               return InvalidOid;              /* no chance for a different operator */
+       if (ltype != indexkeytype && !IS_BINARY_COMPATIBLE(ltype, indexkeytype))
+               return InvalidOid;
+       if (rtype != indexkeytype && !IS_BINARY_COMPATIBLE(rtype, indexkeytype))
+               return InvalidOid;
 
-               if (opname == NULL)
-                       return InvalidOid;      /* probably shouldn't happen */
+       /*
+        * OK, look for operator of the same name with the indexkey's data type.
+        * (In theory this might find a non-semantically-comparable operator,
+        * but in practice that seems pretty unlikely for binary-compatible types.)
+        */
+       newop = oper(opname, indexkeytype, indexkeytype, TRUE);
 
-               /* Use the datatype of the index key */
-               if (indexkey_on_left)
-                       newop = oper(opname, ltype, ltype, TRUE);
-               else
-                       newop = oper(opname, rtype, rtype, TRUE);
+       if (HeapTupleIsValid(newop))
+       {
+               Oid                     new_expr_op = oprid(newop);
 
-               if (HeapTupleIsValid(newop))
+               if (new_expr_op != expr_op)
                {
-                       Oid                     new_expr_op = oprid(newop);
-
-                       if (new_expr_op != expr_op)
-                       {
 
-                               /*
-                                * OK, we found a binary-compatible operator of the same
-                                * name; now does it match the index?
-                                */
-                               if (indexkey_on_left)
-                                       commuted_op = new_expr_op;
-                               else
-                                       commuted_op = get_commutator(new_expr_op);
-                               if (commuted_op == InvalidOid)
-                                       return InvalidOid;
-
-                               if (op_class(commuted_op, opclass, relam))
-                                       return new_expr_op;
-                       }
+                       /*
+                        * OK, we found a binary-compatible operator of the same
+                        * name; now does it match the index?
+                        */
+                       if (indexkey_on_left)
+                               commuted_op = new_expr_op;
+                       else
+                               commuted_op = get_commutator(new_expr_op);
+                       if (commuted_op == InvalidOid)
+                               return InvalidOid;
+
+                       if (op_class(commuted_op, opclass, relam))
+                               return new_expr_op;
                }
        }
 
@@ -1526,13 +1560,22 @@ match_index_to_operand(int indexkey,
                                           RelOptInfo *rel,
                                           IndexOptInfo *index)
 {
+       /*
+        * Ignore any RelabelType node above the indexkey.  This is needed to
+        * be able to apply indexscanning in binary-compatible-operator cases.
+        * Note: we can assume there is at most one RelabelType node;
+        * eval_const_expressions() will have simplified if more than one.
+        */
+       if (operand && IsA(operand, RelabelType))
+               operand = (Var *) ((RelabelType *) operand)->arg;
+
        if (index->indproc == InvalidOid)
        {
 
                /*
-                * Normal index.
+                * Simple index.
                 */
-               if (IsA(operand, Var) &&
+               if (operand && IsA(operand, Var) &&
                        lfirsti(rel->relids) == operand->varno &&
                        indexkey == operand->varattno)
                        return true;
@@ -1541,7 +1584,7 @@ match_index_to_operand(int indexkey,
        }
 
        /*
-        * functional index check
+        * Functional index.
         */
        return function_index_operand((Expr *) operand, rel, index);
 }
@@ -1570,18 +1613,23 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index)
        if (function->funcid != index->indproc)
                return false;
 
-       /*
+       /*----------
         * Check that the arguments correspond to the same arguments used to
-        * create the functional index.  To do this we must check that 1.
-        * refer to the right relation. 2. the args have the right attr.
-        * numbers in the right order.
+        * create the functional index.  To do this we must check that
+        *      1. they refer to the right relation.
+        *      2. the args have the right attr. numbers in the right order.
+        * We must ignore RelabelType nodes above the argument Vars in order
+        * to recognize binary-compatible-function cases correctly.
+        *----------
         */
        i = 0;
        foreach(arg, funcargs)
        {
                Var                *var = (Var *) lfirst(arg);
 
-               if (!IsA(var, Var))
+               if (var && IsA(var, RelabelType))
+                       var = (Var *) ((RelabelType *) var)->arg;
+               if (var == NULL || !IsA(var, Var))
                        return false;
                if (indexKeys[i] == 0)
                        return false;
@@ -1643,7 +1691,7 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index)
  *       additional indexscanable qualifications.
  *
  * The given clause is already known to be a binary opclause having
- * the form (indexkey OP const/param) or (const/param OP indexkey),
+ * the form (indexkey OP pseudoconst) or (pseudoconst OP indexkey),
  * but the OP proved not to be one of the index's opclass operators.
  * Return 'true' if we can do something with it anyway.
  */
index bf91b8d1a6ce753e3f5a69525e96784a5ec4615f..c049f5d86b6a6af4295632b73403c4ac5bdb70f3 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.94 2000/07/12 02:37:08 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.95 2000/08/13 02:50:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -865,6 +865,12 @@ static Node *
 fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index,
                                         Oid *opclass)
 {
+       /*
+        * Remove any binary-compatible relabeling of the indexkey
+        */
+       if (IsA(node, RelabelType))
+               node = ((RelabelType *) node)->arg;
+
        /*
         * We represent index keys by Var nodes having the varno of the base
         * table but varattno equal to the index's attribute number (index
index d2bbff7e6006c532fffac5268ac53b44d55b898c..8ffd35c9bb04f4479cfd72fd2178e001f01e1209 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.48 2000/08/08 15:41:38 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.49 2000/08/13 02:50:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -184,7 +184,7 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
 
                /*
                 * There is only one relation participating in 'clause', so
-                * 'clause' must be a restriction clause for that relation.
+                * 'clause' is a restriction clause for that relation.
                 */
                RelOptInfo *rel = get_base_rel(root, lfirsti(relids));
 
@@ -201,11 +201,11 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
                 */
                check_mergejoinable(restrictinfo);
        }
-       else
+       else if (relids != NIL)
        {
 
                /*
-                * 'clause' is a join clause, since there is more than one atom in
+                * 'clause' is a join clause, since there is more than one rel in
                 * the relid list.      Set additional RestrictInfo fields for
                 * joining.
                 *
@@ -219,8 +219,6 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
 
                /*
                 * Add clause to the join lists of all the relevant relations.
-                * (If, perchance, 'clause' contains NO vars, then nothing will
-                * happen...)
                 */
                add_join_info_to_rels(root, restrictinfo, relids);
 
@@ -232,6 +230,15 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
                 */
                add_vars_to_targetlist(root, vars);
        }
+       else
+       {
+               /*
+                * 'clause' references no rels, and therefore we have no place to
+                * attach it.  This means query_planner() screwed up --- it should
+                * treat variable-less clauses separately.
+                */
+               elog(ERROR, "add_restrict_and_join_to_rel: can't cope with variable-free clause");
+       }
 
        /*
         * If the clause has a mergejoinable operator, then the two sides
index 6b6bc15bda7b5e2dd70705f52e2611cbe31f5ea0..abb468aa8d1dc9a3ed7002b402a79c2eb99120fa 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.57 2000/07/27 04:51:04 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.58 2000/08/13 02:50:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -78,7 +78,8 @@ query_planner(Query *root,
                          List *qual,
                          double tuple_fraction)
 {
-       List       *constant_qual = NIL;
+       List       *noncachable_qual;
+       List       *constant_qual;
        List       *var_only_tlist;
        Plan       *subplan;
 
@@ -106,9 +107,14 @@ query_planner(Query *root,
         * have been optimized away by eval_const_expressions().  What we're
         * mostly interested in here is quals that depend only on outer-level
         * vars, although if the qual reduces to "WHERE FALSE" this path will
-        * also be taken.
+        * also be taken.  We also need a special case for quals that contain
+        * noncachable functions but no vars, such as "WHERE random() < 0.5".
+        * These cannot be treated as normal restriction or join quals, but
+        * they're not constants either.  Instead, attach them to the qpqual
+        * of the top-level plan, so that they get evaluated once per potential
+        * output tuple.
         */
-       qual = pull_constant_clauses(qual, &constant_qual);
+       qual = pull_constant_clauses(qual, &noncachable_qual, &constant_qual);
 
        /*
         * Create a target list that consists solely of (resdom var) target
@@ -128,6 +134,12 @@ query_planner(Query *root,
         */
        subplan = subplanner(root, var_only_tlist, qual, tuple_fraction);
 
+       /*
+        * Handle the noncachable quals.
+        */
+       if (noncachable_qual)
+               subplan->qual = nconc(subplan->qual, noncachable_qual);
+
        /*
         * Build a result node to control the plan if we have constant quals.
         */
@@ -163,7 +175,7 @@ query_planner(Query *root,
  *      for processing a single level of attributes.
  *
  * flat_tlist is the flattened target list
- * qual is the qualification to be satisfied
+ * qual is the qualification to be satisfied (restrict and join quals only)
  * tuple_fraction is the fraction of tuples we expect will be retrieved
  *
  * See query_planner() comments about the interpretation of tuple_fraction.
index adda68b636aaf9d4b4bf7f0593d02af232ea65e1..65e714563601d83e64d5dc79ccaf04f0a8d4114e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.70 2000/08/08 15:41:53 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.71 2000/08/13 02:50:10 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -46,6 +46,7 @@ static bool contain_subplans_walker(Node *node, void *context);
 static bool pull_subplans_walker(Node *node, List **listptr);
 static bool check_subplans_for_ungrouped_vars_walker(Node *node,
                                                                                 Query *context);
+static bool contain_noncachable_functions_walker(Node *node, void *context);
 static int     is_single_func(Node *node);
 static Node *eval_const_expressions_mutator(Node *node, void *context);
 static Expr *simplify_op_or_func(Expr *expr, List *args);
@@ -601,40 +602,135 @@ check_subplans_for_ungrouped_vars_walker(Node *node,
 
 
 /*****************************************************************************
- *                                                                                                                                                      *
- *             General clause-manipulating routines                                                             *
- *                                                                                                                                                      *
+ *             Check clauses for noncachable functions
  *****************************************************************************/
 
-
 /*
- * pull_constant_clauses
- *       Scans through a list of qualifications and find those that
- *       contain no variables (of the current query level).
+ * contain_noncachable_functions
+ *       Recursively search for noncachable functions within a clause.
  *
- * Returns a list of the constant clauses in constantQual and the remaining
- * quals as the return value.
+ * Returns true if any noncachable function (or operator implemented by a
+ * noncachable function) is found.  This test is needed so that we don't
+ * mistakenly think that something like "WHERE random() < 0.5" can be treated
+ * as a constant qualification.
  *
+ * XXX we do not examine sublinks/subplans to see if they contain uses of
+ * noncachable functions.  It's not real clear if that is correct or not...
+ */
+bool
+contain_noncachable_functions(Node *clause)
+{
+       return contain_noncachable_functions_walker(clause, NULL);
+}
+
+static bool
+contain_noncachable_functions_walker(Node *node, void *context)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, Expr))
+       {
+               Expr       *expr = (Expr *) node;
+
+               switch (expr->opType)
+               {
+                       case OP_EXPR:
+                               if (! op_iscachable(((Oper *) expr->oper)->opno))
+                                       return true;
+                               break;
+                       case FUNC_EXPR:
+                               if (! func_iscachable(((Func *) expr->oper)->funcid))
+                                       return true;
+                               break;
+                       default:
+                               break;
+               }
+       }
+       return expression_tree_walker(node, contain_noncachable_functions_walker,
+                                                                 context);
+}
+
+
+/*****************************************************************************
+ *             Check for "pseudo-constant" clauses
+ *****************************************************************************/
+
+/*
+ * is_pseudo_constant_clause
+ *       Detect whether a clause is "constant", ie, it contains no variables
+ *       of the current query level and no uses of noncachable functions.
+ *       Such a clause is not necessarily a true constant: it can still contain
+ *       Params and outer-level Vars.  However, its value will be constant over
+ *       any one scan of the current query, so it can be used as an indexscan
+ *       key or (if a top-level qual) can be pushed up to become a gating qual.
+ */
+bool
+is_pseudo_constant_clause(Node *clause)
+{
+       /*
+        * We could implement this check in one recursive scan.  But since the
+        * check for noncachable functions is both moderately expensive and
+        * unlikely to fail, it seems better to look for Vars first and only
+        * check for noncachable functions if we find no Vars.
+        */
+       if (!contain_var_clause(clause) &&
+               !contain_noncachable_functions(clause))
+               return true;
+       return false;
+}
+
+/*----------
+ * pull_constant_clauses
+ *             Scan through a list of qualifications and separate "constant" quals
+ *             from those that are not.
+ *
+ * The input qual list is divided into three parts:
+ *     * The function's return value is a list of all those quals that contain
+ *       variable(s) of the current query level.  (These quals will become
+ *       restrict and join quals.)
+ *     * *noncachableQual receives a list of quals that have no Vars, yet
+ *       cannot be treated as constants because they contain noncachable
+ *       function calls.  (Example: WHERE random() < 0.5)
+ *     * *constantQual receives a list of the remaining quals, which can be
+ *       treated as constants for any one scan of the current query level.
+ *       (They are really only pseudo-constant, since they may contain
+ *       Params or outer-level Vars.)
+ *----------
  */
 List *
-pull_constant_clauses(List *quals, List **constantQual)
+pull_constant_clauses(List *quals,
+                                         List **noncachableQual,
+                                         List **constantQual)
 {
        List       *q;
+       List       *normqual = NIL;
+       List       *noncachequal = NIL;
        List       *constqual = NIL;
-       List       *restqual = NIL;
 
        foreach(q, quals)
        {
-               if (!contain_var_clause(lfirst(q)))
-                       constqual = lcons(lfirst(q), constqual);
+               Node       *qual = (Node *) lfirst(q);
+
+               if (contain_var_clause(qual))
+                       normqual = lappend(normqual, qual);
+               else if (contain_noncachable_functions(qual))
+                       noncachequal = lappend(noncachequal, qual);
                else
-                       restqual = lcons(lfirst(q), restqual);
+                       constqual = lappend(constqual, qual);
        }
+
+       *noncachableQual = noncachequal;
        *constantQual = constqual;
-       return restqual;
+       return normqual;
 }
 
 
+/*****************************************************************************
+ *                                                                                                                                                      *
+ *             General clause-manipulating routines                                                             *
+ *                                                                                                                                                      *
+ *****************************************************************************/
+
 /*
  * clause_relids_vars
  *       Retrieves distinct relids and vars appearing within a clause.
@@ -744,6 +840,13 @@ get_relattval(Node *clause,
        if (!right)
                goto default_results;
 
+       /* Ignore any binary-compatible relabeling */
+
+       if (IsA(left, RelabelType))
+               left = (Var *) ((RelabelType *) left)->arg;
+       if (IsA(right, RelabelType))
+               right = (Var *) ((RelabelType *) right)->arg;
+
        /* First look for the var or func */
 
        if (IsA(left, Var) &&
@@ -856,6 +959,12 @@ get_rels_atts(Node *clause,
                {
                        int                     funcvarno;
 
+                       /* Ignore any binary-compatible relabeling */
+                       if (IsA(left, RelabelType))
+                               left = (Var *) ((RelabelType *) left)->arg;
+                       if (IsA(right, RelabelType))
+                               right = (Var *) ((RelabelType *) right)->arg;
+
                        if (IsA(left, Var))
                        {
                                *relid1 = left->varno;
@@ -1147,12 +1256,20 @@ eval_const_expressions_mutator(Node *node, void *context)
                /*
                 * If we can simplify the input to a constant, then we don't need
                 * the RelabelType node anymore: just change the type field of the
-                * Const node.  Otherwise, copy the RelabelType node.
+                * Const node.  Otherwise, must copy the RelabelType node.
                 */
                RelabelType *relabel = (RelabelType *) node;
                Node       *arg;
 
                arg = eval_const_expressions_mutator(relabel->arg, context);
+
+               /*
+                * If we find stacked RelabelTypes (eg, from foo :: int :: oid)
+                * we can discard all but the top one.
+                */
+               while (arg && IsA(arg, RelabelType))
+                       arg = ((RelabelType *) arg)->arg;
+
                if (arg && IsA(arg, Const))
                {
                        Const      *con = (Const *) arg;
@@ -1369,7 +1486,10 @@ simplify_op_or_func(Expr *expr, List *args)
                funcid = func->funcid;
                result_typeid = func->functype;
        }
-       /* Someday lsyscache.c might provide a function for this */
+       /*
+        * we could use func_iscachable() here, but we need several fields
+        * out of the func tuple, so might as well just look it up once.
+        */
        func_tuple = SearchSysCacheTuple(PROCOID,
                                                                         ObjectIdGetDatum(funcid),
                                                                         0, 0, 0);
index 5c80a5179a43cf14d07c98c8417a085cd9f5c4fc..28910dae892036c364cf21b1c3380babb652a733 100644 (file)
@@ -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.44 2000/07/23 03:50:26 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.45 2000/08/13 02:50:16 tgl Exp $
  *
  * NOTES
  *       Eventually, the index information should go through here, too.
 /*
  * op_class
  *
- *             Return t iff operator 'opid' is in operator class 'opclass' for
+ *             Return t iff operator 'opno' is in operator class 'opclass' for
  *             access method 'amopid'.
  */
 bool
-op_class(Oid opid, Oid opclass, Oid amopid)
+op_class(Oid opno, Oid opclass, Oid amopid)
 {
        if (HeapTupleIsValid(SearchSysCacheTuple(AMOPOPID,
                                                                                         ObjectIdGetDatum(opclass),
-                                                                                        ObjectIdGetDatum(opid),
+                                                                                        ObjectIdGetDatum(opno),
                                                                                         ObjectIdGetDatum(amopid),
                                                                                         0)))
                return true;
@@ -287,7 +287,7 @@ get_opcode(Oid opno)
                return optup->oprcode;
        }
        else
-               return (RegProcedure) NULL;
+               return (RegProcedure) InvalidOid;
 }
 
 /*
@@ -371,17 +371,28 @@ op_hashjoinable(Oid opno, Oid ltype, Oid rtype)
        return InvalidOid;
 }
 
+/*
+ * op_iscachable
+ *
+ * Get the proiscachable flag for the operator's underlying function.
+ */
+bool
+op_iscachable(Oid opno)
+{
+       RegProcedure    funcid = get_opcode(opno);
+
+       if (funcid == (RegProcedure) InvalidOid)
+               elog(ERROR, "Operator OID %u does not exist", opno);
+
+       return func_iscachable((Oid) funcid);
+}
+
 HeapTuple
 get_operator_tuple(Oid opno)
 {
-       HeapTuple       optup;
-
-       if ((optup = SearchSysCacheTuple(OPEROID,
-                                                                        ObjectIdGetDatum(opno),
-                                                                        0, 0, 0)))
-               return optup;
-       else
-               return (HeapTuple) NULL;
+       return SearchSysCacheTuple(OPEROID,
+                                                          ObjectIdGetDatum(opno),
+                                                          0, 0, 0);
 }
 
 /*
@@ -450,7 +461,7 @@ get_oprrest(Oid opno)
                return optup->oprrest;
        }
        else
-               return (RegProcedure) NULL;
+               return (RegProcedure) InvalidOid;
 }
 
 /*
@@ -473,7 +484,7 @@ get_oprjoin(Oid opno)
                return optup->oprjoin;
        }
        else
-               return (RegProcedure) NULL;
+               return (RegProcedure) InvalidOid;
 }
 
 /*                             ---------- FUNCTION CACHE ----------                                     */
@@ -496,6 +507,24 @@ get_func_rettype(Oid funcid)
        return ((Form_pg_proc) GETSTRUCT(func_tuple))->prorettype;
 }
 
+/*
+ * func_iscachable
+ *             Given procedure id, return the function's proiscachable flag.
+ */
+bool
+func_iscachable(Oid funcid)
+{
+       HeapTuple       func_tuple;
+
+       func_tuple = SearchSysCacheTuple(PROCOID,
+                                                                        ObjectIdGetDatum(funcid),
+                                                                        0, 0, 0);
+       if (!HeapTupleIsValid(func_tuple))
+               elog(ERROR, "Function OID %u does not exist", funcid);
+
+       return ((Form_pg_proc) GETSTRUCT(func_tuple))->proiscachable;
+}
+
 /*                             ---------- RELATION CACHE ----------                                     */
 
 #ifdef NOT_USED
index b1242b0da0d7efdcb35114c8601758af7d3dba1e..42e3fae8335e0bba2171888ef9e71aea3c84292b 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeNestloop.h,v 1.12 2000/01/26 05:58:05 momjian Exp $
+ * $Id: nodeNestloop.h,v 1.13 2000/08/13 02:50:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,7 +16,7 @@
 
 #include "nodes/plannodes.h"
 
-extern TupleTableSlot *ExecNestLoop(NestLoop *node, Plan *parent);
+extern TupleTableSlot *ExecNestLoop(NestLoop *node);
 extern bool ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent);
 extern int     ExecCountSlotsNestLoop(NestLoop *node);
 extern void ExecEndNestLoop(NestLoop *node);
index a9ef6e78ae3d99a41b1f92452a53af2f2aae38e8..6e691c1d786c9f97e83a11b0aa18fd3fac6d605f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.45 2000/08/06 04:26:29 tgl Exp $
+ * $Id: execnodes.h,v 1.46 2000/08/13 02:50:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -380,6 +380,7 @@ typedef struct CommonScanState
  *             NumScanKeys                array of no of keys in each Skey struct
  *             RuntimeKeyInfo     array of array of flags for Skeys evaled at runtime
  *             RuntimeContext     expr context for evaling runtime Skeys
+ *             RuntimeKeysReady   true if runtime Skeys have been computed
  *             RelationDescs      ptr to array of relation descriptors
  *             ScanDescs                  ptr to array of scan descriptors
  * ----------------
@@ -394,6 +395,7 @@ typedef struct IndexScanState
        int                *iss_NumScanKeys;
        int               **iss_RuntimeKeyInfo;
        ExprContext *iss_RuntimeContext;
+       bool            iss_RuntimeKeysReady;
        RelationPtr iss_RelationDescs;
        IndexScanDescPtr iss_ScanDescs;
        HeapTupleData iss_htup;
index 946a9b0586ff39c1fd2e76f131b9dc34ba6b7978..1b2bcd920551f0d8850f2761548ecc15d97121d1 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: clauses.h,v 1.37 2000/05/30 04:24:57 tgl Exp $
+ * $Id: clauses.h,v 1.38 2000/08/13 02:50:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,7 +53,13 @@ extern bool contain_subplans(Node *clause);
 extern List *pull_subplans(Node *clause);
 extern void check_subplans_for_ungrouped_vars(Node *clause, Query *query);
 
-extern List *pull_constant_clauses(List *quals, List **constantQual);
+extern bool contain_noncachable_functions(Node *clause);
+
+extern bool is_pseudo_constant_clause(Node *clause);
+
+extern List *pull_constant_clauses(List *quals,
+                                                                  List **noncachableQual,
+                                                                  List **constantQual);
 
 extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars);
 extern int     NumRelids(Node *clause);
index 5c6c09ccbc3f0081e6bfd1e777c0149d298b081d..9c105576ae2256ba79e099e44a9f3f9a02e7a6dc 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: lsyscache.h,v 1.24 2000/06/08 22:37:58 momjian Exp $
+ * $Id: lsyscache.h,v 1.25 2000/08/13 02:50:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,7 +15,7 @@
 
 #include "access/htup.h"
 
-extern bool op_class(Oid opid, Oid opclass, Oid amopid);
+extern bool op_class(Oid opno, Oid opclass, Oid amopid);
 extern char *get_attname(Oid relid, AttrNumber attnum);
 extern AttrNumber get_attnum(Oid relid, char *attname);
 extern Oid     get_atttype(Oid relid, AttrNumber attnum);
@@ -23,21 +23,20 @@ extern bool get_attisset(Oid relid, char *attname);
 extern int32 get_atttypmod(Oid relid, AttrNumber attnum);
 extern double get_attdisbursion(Oid relid, AttrNumber attnum,
                                  double min_estimate);
-extern RegProcedure get_opcode(Oid opid);
-extern char *get_opname(Oid opid);
-extern bool op_mergejoinable(Oid opid, Oid ltype, Oid rtype,
+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 Oid     op_hashjoinable(Oid opid, Oid ltype, Oid rtype);
-extern Oid     get_commutator(Oid opid);
+extern Oid     op_hashjoinable(Oid opno, Oid ltype, Oid rtype);
+extern bool op_iscachable(Oid opno);
 extern HeapTuple get_operator_tuple(Oid opno);
-extern Oid     get_negator(Oid opid);
-extern RegProcedure get_oprrest(Oid opid);
-extern RegProcedure get_oprjoin(Oid opid);
+extern Oid     get_commutator(Oid opno);
+extern Oid     get_negator(Oid opno);
+extern RegProcedure get_oprrest(Oid opno);
+extern RegProcedure get_oprjoin(Oid opno);
 extern Oid     get_func_rettype(Oid funcid);
+extern bool func_iscachable(Oid funcid);
 extern char *get_rel_name(Oid relid);
-extern struct varlena *get_relstub(Oid relid, int no, bool *islast);
-extern Oid     get_ruleid(char *rulename);
-extern Oid     get_eventrelid(Oid ruleid);
 extern int16 get_typlen(Oid typid);
 extern bool get_typbyval(Oid typid);
 extern Datum get_typdefault(Oid typid);
index 1f54890e5c536e098a92b766eb60b8b6d8cbda5e..0096b3825c63019e0aac39c97e3bb0b0d285a401 100644 (file)
@@ -3,7 +3,7 @@
  *                       procedural language
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.26 2000/08/03 16:34:57 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.27 2000/08/13 02:50:35 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -54,6 +54,7 @@
 #include "executor/spi.h"
 #include "executor/spi_priv.h"
 #include "fmgr.h"
+#include "parser/parse_expr.h"
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
@@ -2487,6 +2488,9 @@ exec_simple_check_node(Node *node)
                case T_Const:
                        return TRUE;
 
+               case T_RelabelType:
+                       return exec_simple_check_node(((RelabelType *) node)->arg);
+
                default:
                        return FALSE;
        }
@@ -2563,26 +2567,7 @@ exec_simple_check_plan(PLpgSQL_expr * expr)
         * ----------
         */
        expr->plan_simple_expr = tle->expr;
-
-       switch (nodeTag(tle->expr))
-       {
-               case T_Expr:
-                       expr->plan_simple_type =
-                               ((Expr *) (tle->expr))->typeOid;
-                       break;
-
-               case T_Param:
-                       expr->plan_simple_type =
-                               ((Param *) (tle->expr))->paramtype;
-                       break;
-
-               case T_Const:
-                       expr->plan_simple_type = ((Const *) (tle->expr))->consttype;
-                       break;
-
-               default:
-                       expr->plan_simple_type = InvalidOid;
-       }
+       expr->plan_simple_type = exprType(tle->expr);
 }