mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-02-05 19:09:58 +08:00
Clean up handling of variable-free qual clauses. System now does the
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.
This commit is contained in:
parent
766fb7f707
commit
37168b8da4
@ -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:
|
||||
|
@ -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.
|
||||
* ----------------
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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))
|
||||
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;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
if (HeapTupleIsValid(newop))
|
||||
{
|
||||
char *opname = get_opname(expr_op);
|
||||
Operator newop;
|
||||
Oid new_expr_op = oprid(newop);
|
||||
|
||||
if (opname == NULL)
|
||||
return InvalidOid; /* probably shouldn't happen */
|
||||
|
||||
/* 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))
|
||||
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;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
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.
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
@ -600,41 +601,136 @@ check_subplans_for_ungrouped_vars_walker(Node *node,
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Check clauses for noncachable functions
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* contain_noncachable_functions
|
||||
* Recursively search for noncachable functions within a clause.
|
||||
*
|
||||
* 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 **noncachableQual,
|
||||
List **constantQual)
|
||||
{
|
||||
List *q;
|
||||
List *normqual = NIL;
|
||||
List *noncachequal = NIL;
|
||||
List *constqual = NIL;
|
||||
|
||||
foreach(q, quals)
|
||||
{
|
||||
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
|
||||
constqual = lappend(constqual, qual);
|
||||
}
|
||||
|
||||
*noncachableQual = noncachequal;
|
||||
*constantQual = constqual;
|
||||
return normqual;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* *
|
||||
* General clause-manipulating routines *
|
||||
* *
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
/*
|
||||
* pull_constant_clauses
|
||||
* Scans through a list of qualifications and find those that
|
||||
* contain no variables (of the current query level).
|
||||
*
|
||||
* Returns a list of the constant clauses in constantQual and the remaining
|
||||
* quals as the return value.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
pull_constant_clauses(List *quals, List **constantQual)
|
||||
{
|
||||
List *q;
|
||||
List *constqual = NIL;
|
||||
List *restqual = NIL;
|
||||
|
||||
foreach(q, quals)
|
||||
{
|
||||
if (!contain_var_clause(lfirst(q)))
|
||||
constqual = lcons(lfirst(q), constqual);
|
||||
else
|
||||
restqual = lcons(lfirst(q), restqual);
|
||||
}
|
||||
*constantQual = constqual;
|
||||
return restqual;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
59
src/backend/utils/cache/lsyscache.c
vendored
59
src/backend/utils/cache/lsyscache.c
vendored
@ -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.
|
||||
@ -26,15 +26,15 @@
|
||||
/*
|
||||
* 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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user