Fix problem in which sloppily-coded test in ExecInitIndexScan would
think that both sides of indexqual look like index keys. An example is create table inside (f1 float8 primary key); create table outside (g1 float8, g2 float8); select * from inside,outside where f1 = atan2(g1+1, g2); ERROR: ExecInitIndexScan: both left and right ops are rel-vars (note that failure is potentially platform-dependent). Solution is a cleanup I had had in mind to make anyway: functional index keys should be represented as Var nodes in the fixed indexqual, just like regular index keys.
This commit is contained in:
parent
1c8244ec67
commit
1c5b902018
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.49 2000/04/12 17:15:09 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.50 2000/05/23 16:56:37 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -734,8 +734,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
|||||||
*/
|
*/
|
||||||
for (j = 0; j < n_keys; j++)
|
for (j = 0; j < n_keys; j++)
|
||||||
{
|
{
|
||||||
Expr *clause; /* one part of index qual */
|
Expr *clause; /* one clause of index qual */
|
||||||
Oper *op; /* operator used in scan.. */
|
Oper *op; /* operator used in clause */
|
||||||
Node *leftop; /* expr on lhs of operator */
|
Node *leftop; /* expr on lhs of operator */
|
||||||
Node *rightop;/* expr on rhs ... */
|
Node *rightop;/* expr on rhs ... */
|
||||||
bits16 flags = 0;
|
bits16 flags = 0;
|
||||||
@ -794,6 +794,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
scanvar = NO_OP;
|
scanvar = NO_OP;
|
||||||
|
run_keys[j] = NO_OP;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* determine information in leftop
|
* determine information in leftop
|
||||||
@ -803,7 +804,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
|||||||
|
|
||||||
Assert(leftop != NULL);
|
Assert(leftop != NULL);
|
||||||
|
|
||||||
if (IsA(leftop, Var) &&var_is_rel((Var *) leftop))
|
if (IsA(leftop, Var) && var_is_rel((Var *) leftop))
|
||||||
{
|
{
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* if the leftop is a "rel-var", then it means
|
* if the leftop is a "rel-var", then it means
|
||||||
@ -814,19 +815,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
|||||||
varattno = ((Var *) leftop)->varattno;
|
varattno = ((Var *) leftop)->varattno;
|
||||||
scanvar = LEFT_OP;
|
scanvar = LEFT_OP;
|
||||||
}
|
}
|
||||||
else if (is_funcclause(leftop) &&
|
|
||||||
var_is_rel(lfirst(((Expr *) leftop)->args)))
|
|
||||||
{
|
|
||||||
/* ----------------
|
|
||||||
* if the leftop is a func node then it means
|
|
||||||
* it identifies the value to place in our scan key.
|
|
||||||
* Since functional indices have only one attribute
|
|
||||||
* the attno must always be set to 1.
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
varattno = 1;
|
|
||||||
scanvar = LEFT_OP;
|
|
||||||
}
|
|
||||||
else if (IsA(leftop, Const))
|
else if (IsA(leftop, Const))
|
||||||
{
|
{
|
||||||
/* ----------------
|
/* ----------------
|
||||||
@ -834,8 +822,9 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
|||||||
* it identifies the value to place in our scan key.
|
* it identifies the value to place in our scan key.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
run_keys[j] = NO_OP;
|
|
||||||
scanvalue = ((Const *) leftop)->constvalue;
|
scanvalue = ((Const *) leftop)->constvalue;
|
||||||
|
if (((Const *) leftop)->constisnull)
|
||||||
|
flags |= SK_ISNULL;
|
||||||
}
|
}
|
||||||
else if (IsA(leftop, Param))
|
else if (IsA(leftop, Param))
|
||||||
{
|
{
|
||||||
@ -850,32 +839,31 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
|||||||
/* Life was so easy before ... subselects */
|
/* Life was so easy before ... subselects */
|
||||||
if (((Param *) leftop)->paramkind == PARAM_EXEC)
|
if (((Param *) leftop)->paramkind == PARAM_EXEC)
|
||||||
{
|
{
|
||||||
|
/* treat Param as runtime key */
|
||||||
have_runtime_keys = true;
|
have_runtime_keys = true;
|
||||||
run_keys[j] = LEFT_OP;
|
run_keys[j] = LEFT_OP;
|
||||||
execParam = lappendi(execParam, ((Param *) leftop)->paramid);
|
execParam = lappendi(execParam, ((Param *) leftop)->paramid);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
/* treat Param like a constant */
|
||||||
scanvalue = ExecEvalParam((Param *) leftop,
|
scanvalue = ExecEvalParam((Param *) leftop,
|
||||||
scanstate->cstate.cs_ExprContext,
|
scanstate->cstate.cs_ExprContext,
|
||||||
&isnull);
|
&isnull);
|
||||||
if (isnull)
|
if (isnull)
|
||||||
flags |= SK_ISNULL;
|
flags |= SK_ISNULL;
|
||||||
|
|
||||||
run_keys[j] = NO_OP;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* otherwise, the leftop contains information usable
|
* otherwise, the leftop contains an expression evaluable
|
||||||
* at runtime to figure out the value to place in our
|
* at runtime to figure out the value to place in our
|
||||||
* scan key.
|
* scan key.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
have_runtime_keys = true;
|
have_runtime_keys = true;
|
||||||
run_keys[j] = LEFT_OP;
|
run_keys[j] = LEFT_OP;
|
||||||
scanvalue = Int32GetDatum((int32) true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
@ -886,7 +874,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
|||||||
|
|
||||||
Assert(rightop != NULL);
|
Assert(rightop != NULL);
|
||||||
|
|
||||||
if (IsA(rightop, Var) &&var_is_rel((Var *) rightop))
|
if (IsA(rightop, Var) && var_is_rel((Var *) rightop))
|
||||||
{
|
{
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* here we make sure only one op identifies the
|
* here we make sure only one op identifies the
|
||||||
@ -906,23 +894,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
|||||||
varattno = ((Var *) rightop)->varattno;
|
varattno = ((Var *) rightop)->varattno;
|
||||||
scanvar = RIGHT_OP;
|
scanvar = RIGHT_OP;
|
||||||
}
|
}
|
||||||
else if (is_funcclause(rightop) &&
|
|
||||||
var_is_rel(lfirst(((Expr *) rightop)->args)))
|
|
||||||
{
|
|
||||||
/* ----------------
|
|
||||||
* if the rightop is a func node then it means
|
|
||||||
* it identifies the value to place in our scan key.
|
|
||||||
* Since functional indices have only one attribute
|
|
||||||
* the attno must always be set to 1.
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
if (scanvar == LEFT_OP)
|
|
||||||
elog(ERROR, "ExecInitIndexScan: %s",
|
|
||||||
"both left and right ops are rel-vars");
|
|
||||||
|
|
||||||
varattno = 1;
|
|
||||||
scanvar = RIGHT_OP;
|
|
||||||
}
|
|
||||||
else if (IsA(rightop, Const))
|
else if (IsA(rightop, Const))
|
||||||
{
|
{
|
||||||
/* ----------------
|
/* ----------------
|
||||||
@ -930,8 +901,9 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
|||||||
* it identifies the value to place in our scan key.
|
* it identifies the value to place in our scan key.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
run_keys[j] = NO_OP;
|
|
||||||
scanvalue = ((Const *) rightop)->constvalue;
|
scanvalue = ((Const *) rightop)->constvalue;
|
||||||
|
if (((Const *) rightop)->constisnull)
|
||||||
|
flags |= SK_ISNULL;
|
||||||
}
|
}
|
||||||
else if (IsA(rightop, Param))
|
else if (IsA(rightop, Param))
|
||||||
{
|
{
|
||||||
@ -946,32 +918,31 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
|||||||
/* Life was so easy before ... subselects */
|
/* Life was so easy before ... subselects */
|
||||||
if (((Param *) rightop)->paramkind == PARAM_EXEC)
|
if (((Param *) rightop)->paramkind == PARAM_EXEC)
|
||||||
{
|
{
|
||||||
|
/* treat Param as runtime key */
|
||||||
have_runtime_keys = true;
|
have_runtime_keys = true;
|
||||||
run_keys[j] = RIGHT_OP;
|
run_keys[j] = RIGHT_OP;
|
||||||
execParam = lappendi(execParam, ((Param *) rightop)->paramid);
|
execParam = lappendi(execParam, ((Param *) rightop)->paramid);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
/* treat Param like a constant */
|
||||||
scanvalue = ExecEvalParam((Param *) rightop,
|
scanvalue = ExecEvalParam((Param *) rightop,
|
||||||
scanstate->cstate.cs_ExprContext,
|
scanstate->cstate.cs_ExprContext,
|
||||||
&isnull);
|
&isnull);
|
||||||
if (isnull)
|
if (isnull)
|
||||||
flags |= SK_ISNULL;
|
flags |= SK_ISNULL;
|
||||||
|
|
||||||
run_keys[j] = NO_OP;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* otherwise, the rightop contains information usable
|
* otherwise, the rightop contains an expression evaluable
|
||||||
* at runtime to figure out the value to place in our
|
* at runtime to figure out the value to place in our
|
||||||
* scan key.
|
* scan key.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
have_runtime_keys = true;
|
have_runtime_keys = true;
|
||||||
run_keys[j] = RIGHT_OP;
|
run_keys[j] = RIGHT_OP;
|
||||||
scanvalue = Int32GetDatum((int32) true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
@ -992,7 +963,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
|||||||
varattno, /* attribute number to
|
varattno, /* attribute number to
|
||||||
* scan */
|
* scan */
|
||||||
(RegProcedure) opid, /* reg proc to use */
|
(RegProcedure) opid, /* reg proc to use */
|
||||||
(Datum) scanvalue); /* constant */
|
scanvalue); /* constant */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.89 2000/04/12 17:15:21 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.90 2000/05/23 16:56:36 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -27,6 +27,7 @@
|
|||||||
#include "optimizer/planmain.h"
|
#include "optimizer/planmain.h"
|
||||||
#include "optimizer/restrictinfo.h"
|
#include "optimizer/restrictinfo.h"
|
||||||
#include "optimizer/tlist.h"
|
#include "optimizer/tlist.h"
|
||||||
|
#include "parser/parse_expr.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
@ -722,7 +723,7 @@ create_hashjoin_node(HashPath *best_path,
|
|||||||
* machinery needs.
|
* machinery needs.
|
||||||
*
|
*
|
||||||
* We have three tasks here:
|
* We have three tasks here:
|
||||||
* * Var nodes representing index keys must have varattno equal to the
|
* * Index keys must be represented by Var nodes with varattno set to the
|
||||||
* index's attribute number, not the attribute number in the original rel.
|
* index's attribute number, not the attribute number in the original rel.
|
||||||
* * indxpath.c may have selected an index that is binary-compatible with
|
* * indxpath.c may have selected an index that is binary-compatible with
|
||||||
* the actual expression operator, but not exactly the same datatype.
|
* the actual expression operator, but not exactly the same datatype.
|
||||||
@ -789,7 +790,7 @@ fix_indxqual_references(List *indexquals, IndexPath *index_path)
|
|||||||
* Fix the sublist of indexquals to be used in a particular scan.
|
* Fix the sublist of indexquals to be used in a particular scan.
|
||||||
*
|
*
|
||||||
* For each qual clause, commute if needed to put the indexkey operand on the
|
* For each qual clause, commute if needed to put the indexkey operand on the
|
||||||
* left, and then change its varno. (We do not need to change the other side
|
* left, and then fix its varattno. (We do not need to change the other side
|
||||||
* of the clause.) Also change the operator if necessary.
|
* of the clause.) Also change the operator if necessary.
|
||||||
*/
|
*/
|
||||||
static List *
|
static List *
|
||||||
@ -863,8 +864,16 @@ static Node *
|
|||||||
fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index,
|
fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index,
|
||||||
Oid *opclass)
|
Oid *opclass)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* We represent index keys by Var nodes having the varno of the base
|
||||||
|
* table but varattno equal to the index's attribute number (index
|
||||||
|
* column position). This is a bit hokey ... would be cleaner to use
|
||||||
|
* a special-purpose node type that could not be mistaken for a regular
|
||||||
|
* Var. But it will do for now.
|
||||||
|
*/
|
||||||
if (IsA(node, Var))
|
if (IsA(node, Var))
|
||||||
{
|
{
|
||||||
|
/* If it's a var, find which index key position it occupies */
|
||||||
if (((Var *) node)->varno == baserelid)
|
if (((Var *) node)->varno == baserelid)
|
||||||
{
|
{
|
||||||
int varatt = ((Var *) node)->varattno;
|
int varatt = ((Var *) node)->varattno;
|
||||||
@ -877,6 +886,7 @@ fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index,
|
|||||||
Node *newnode = copyObject(node);
|
Node *newnode = copyObject(node);
|
||||||
|
|
||||||
((Var *) newnode)->varattno = pos + 1;
|
((Var *) newnode)->varattno = pos + 1;
|
||||||
|
/* return the correct opclass, too */
|
||||||
*opclass = index->indclass[pos];
|
*opclass = index->indclass[pos];
|
||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
@ -890,22 +900,17 @@ fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Else, it must be a func expression representing a functional index.
|
* Else, it must be a func expression matching a functional index.
|
||||||
*
|
* Since we currently only support single-column functional indexes,
|
||||||
* Currently, there is no need for us to do anything here for functional
|
* the returned varattno must be 1.
|
||||||
* indexes. If nodeIndexscan.c sees a func clause as the left or
|
|
||||||
* right-hand toplevel operand of an indexqual, it assumes that that
|
|
||||||
* is a reference to the functional index's value and makes the
|
|
||||||
* appropriate substitution. (It would be cleaner to make the
|
|
||||||
* substitution here, I think --- suspect this issue if a join clause
|
|
||||||
* involving a function call misbehaves...)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
Assert(is_funcclause(node)); /* not a very thorough check, but easy */
|
||||||
|
|
||||||
/* indclass[0] is the only class of a functional index */
|
/* indclass[0] is the only class of a functional index */
|
||||||
*opclass = index->indclass[0];
|
*opclass = index->indclass[0];
|
||||||
|
|
||||||
/* return the unmodified node */
|
return (Node *) makeVar(baserelid, 1, exprType(node), -1, 0);
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user