
Now You can do queries like select sum(some_func(x)) from ... select min(table1.x + table2.y) from table1, table2 where ... and so on. Vadim
757 lines
17 KiB
C
757 lines
17 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* clauses.c--
|
|
* routines to manipulate qualification clauses
|
|
*
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.5 1996/11/30 17:48:52 momjian Exp $
|
|
*
|
|
* HISTORY
|
|
* AUTHOR DATE MAJOR EVENT
|
|
* Andrew Yu Nov 3, 1994 clause.c and clauses.c combined
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include <catalog/pg_operator.h>
|
|
#include "nodes/primnodes.h"
|
|
#include "nodes/relation.h"
|
|
#include "nodes/parsenodes.h"
|
|
#include "nodes/makefuncs.h"
|
|
#include "nodes/nodeFuncs.h"
|
|
|
|
#include "catalog/pg_aggregate.h"
|
|
|
|
#include "utils/syscache.h"
|
|
#include "utils/lsyscache.h"
|
|
|
|
#include "optimizer/clauses.h"
|
|
#include "optimizer/internal.h"
|
|
#include "optimizer/var.h"
|
|
|
|
|
|
Expr *
|
|
make_clause(int type, Node *oper, List *args)
|
|
{
|
|
if (type == AND_EXPR || type == OR_EXPR || type == NOT_EXPR ||
|
|
type == OP_EXPR || type == FUNC_EXPR) {
|
|
Expr *expr = makeNode(Expr);
|
|
|
|
/*
|
|
* assume type checking already done and we don't need the type of
|
|
* the expr any more.
|
|
*/
|
|
expr->typeOid = InvalidOid;
|
|
expr->opType = type;
|
|
expr->oper = oper; /* ignored for AND, OR, NOT */
|
|
expr->args = args;
|
|
return expr;
|
|
}else {
|
|
/* will this ever happen? translated from lispy C code - ay 10/94 */
|
|
return((Expr*)args);
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* OPERATOR clause functions
|
|
*****************************************************************************/
|
|
|
|
|
|
/*
|
|
* is_opclause--
|
|
*
|
|
* Returns t iff the clause is an operator clause:
|
|
* (op expr expr) or (op expr).
|
|
*
|
|
* [historical note: is_clause has the exact functionality and is used
|
|
* throughout the code. They're renamed to is_opclause for clarity.
|
|
* - ay 10/94.]
|
|
*/
|
|
bool
|
|
is_opclause(Node *clause)
|
|
{
|
|
return
|
|
(clause!=NULL &&
|
|
nodeTag(clause)==T_Expr && ((Expr*)clause)->opType==OP_EXPR);
|
|
}
|
|
|
|
/*
|
|
* make_opclause--
|
|
* Creates a clause given its operator left operand and right
|
|
* operand (if it is non-null).
|
|
*
|
|
*/
|
|
Expr *
|
|
make_opclause(Oper *op, Var *leftop, Var *rightop)
|
|
{
|
|
Expr *expr = makeNode(Expr);
|
|
|
|
expr->typeOid = InvalidOid; /* assume type checking done */
|
|
expr->opType = OP_EXPR;
|
|
expr->oper = (Node*)op;
|
|
expr->args = makeList(leftop, rightop, -1);
|
|
return expr;
|
|
}
|
|
|
|
/*
|
|
* get_leftop--
|
|
*
|
|
* Returns the left operand of a clause of the form (op expr expr)
|
|
* or (op expr)
|
|
* NB: it is assumed (for now) that all expr must be Var nodes
|
|
*/
|
|
Var *
|
|
get_leftop(Expr *clause)
|
|
{
|
|
if (clause->args!=NULL)
|
|
return(lfirst(clause->args));
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* get_rightop
|
|
*
|
|
* Returns the right operand in a clause of the form (op expr expr).
|
|
*
|
|
*/
|
|
Var *
|
|
get_rightop(Expr *clause)
|
|
{
|
|
if (clause->args!=NULL && lnext(clause->args)!=NULL)
|
|
return (lfirst(lnext(clause->args)));
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* AGG clause functions
|
|
*****************************************************************************/
|
|
|
|
bool
|
|
agg_clause(Node *clause)
|
|
{
|
|
return
|
|
(clause!=NULL && nodeTag(clause)==T_Aggreg);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FUNC clause functions
|
|
*****************************************************************************/
|
|
|
|
/*
|
|
* is_funcclause--
|
|
*
|
|
* Returns t iff the clause is a function clause: (func { expr }).
|
|
*
|
|
*/
|
|
bool
|
|
is_funcclause(Node *clause)
|
|
{
|
|
return
|
|
(clause!=NULL &&
|
|
nodeTag(clause)==T_Expr && ((Expr*)clause)->opType==FUNC_EXPR);
|
|
}
|
|
|
|
/*
|
|
* make_funcclause--
|
|
*
|
|
* Creates a function clause given the FUNC node and the functional
|
|
* arguments.
|
|
*
|
|
*/
|
|
Expr *
|
|
make_funcclause(Func *func, List *funcargs)
|
|
{
|
|
Expr *expr = makeNode(Expr);
|
|
|
|
expr->typeOid = InvalidOid; /* assume type checking done */
|
|
expr->opType = FUNC_EXPR;
|
|
expr->oper = (Node*)func;
|
|
expr->args = funcargs;
|
|
return expr;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* OR clause functions
|
|
*****************************************************************************/
|
|
|
|
/*
|
|
* or_clause--
|
|
*
|
|
* Returns t iff the clause is an 'or' clause: (OR { expr }).
|
|
*
|
|
*/
|
|
bool
|
|
or_clause(Node *clause)
|
|
{
|
|
return
|
|
(clause!=NULL &&
|
|
nodeTag(clause)==T_Expr && ((Expr*)clause)->opType==OR_EXPR);
|
|
}
|
|
|
|
/*
|
|
* make_orclause--
|
|
*
|
|
* Creates an 'or' clause given a list of its subclauses.
|
|
*
|
|
*/
|
|
Expr *
|
|
make_orclause(List *orclauses)
|
|
{
|
|
Expr *expr = makeNode(Expr);
|
|
|
|
expr->typeOid = InvalidOid; /* assume type checking done */
|
|
expr->opType = OR_EXPR;
|
|
expr->oper = NULL;
|
|
expr->args = orclauses;
|
|
return expr;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* NOT clause functions
|
|
*****************************************************************************/
|
|
|
|
/*
|
|
* not_clause--
|
|
*
|
|
* Returns t iff this is a 'not' clause: (NOT expr).
|
|
*
|
|
*/
|
|
bool
|
|
not_clause(Node *clause)
|
|
{
|
|
return
|
|
(clause!=NULL &&
|
|
nodeTag(clause)==T_Expr && ((Expr*)clause)->opType == NOT_EXPR);
|
|
}
|
|
|
|
/*
|
|
* make_notclause--
|
|
*
|
|
* Create a 'not' clause given the expression to be negated.
|
|
*
|
|
*/
|
|
Expr *
|
|
make_notclause(Expr *notclause)
|
|
{
|
|
Expr *expr = makeNode(Expr);
|
|
|
|
expr->typeOid = InvalidOid; /* assume type checking done */
|
|
expr->opType = NOT_EXPR;
|
|
expr->oper = NULL;
|
|
expr->args = lcons(notclause, NIL);
|
|
return expr;
|
|
}
|
|
|
|
/*
|
|
* get_notclausearg--
|
|
*
|
|
* Retrieve the clause within a 'not' clause
|
|
*
|
|
*/
|
|
Expr *
|
|
get_notclausearg(Expr *notclause)
|
|
{
|
|
return(lfirst(notclause->args));
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* AND clause functions
|
|
*****************************************************************************/
|
|
|
|
|
|
/*
|
|
* and_clause--
|
|
*
|
|
* Returns t iff its argument is an 'and' clause: (AND { expr }).
|
|
*
|
|
*/
|
|
bool
|
|
and_clause(Node *clause)
|
|
{
|
|
return
|
|
(clause!=NULL &&
|
|
nodeTag(clause)==T_Expr && ((Expr*)clause)->opType == AND_EXPR);
|
|
}
|
|
/*
|
|
* make_andclause--
|
|
*
|
|
* Create an 'and' clause given its arguments in a list.
|
|
*
|
|
*/
|
|
Expr *
|
|
make_andclause(List *andclauses)
|
|
{
|
|
Expr *expr = makeNode(Expr);
|
|
|
|
expr->typeOid = InvalidOid; /* assume type checking done */
|
|
expr->opType = AND_EXPR;
|
|
expr->oper = NULL;
|
|
expr->args = andclauses;
|
|
return expr;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* *
|
|
* *
|
|
* *
|
|
*****************************************************************************/
|
|
|
|
|
|
/*
|
|
* pull-constant-clauses--
|
|
* Scans through a list of qualifications and find those that
|
|
* contain no variables.
|
|
*
|
|
* 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);
|
|
}
|
|
}
|
|
freeList(quals);
|
|
*constantQual = constqual;
|
|
return restqual;
|
|
}
|
|
|
|
/*
|
|
* clause-relids-vars--
|
|
* Retrieves relids and vars appearing within a clause.
|
|
* Returns ((relid1 relid2 ... relidn) (var1 var2 ... varm)) where
|
|
* vars appear in the clause this is done by recursively searching
|
|
* through the left and right operands of a clause.
|
|
*
|
|
* Returns the list of relids and vars.
|
|
*
|
|
* XXX take the nreverse's out later
|
|
*
|
|
*/
|
|
void
|
|
clause_relids_vars(Node *clause, List **relids, List **vars)
|
|
{
|
|
List *clvars = pull_var_clause(clause);
|
|
List *var_list = NIL;
|
|
List *varno_list = NIL;
|
|
List *i = NIL;
|
|
|
|
foreach (i, clvars) {
|
|
Var *var = (Var *)lfirst(i);
|
|
List *vi;
|
|
|
|
if (!intMember(var->varno, varno_list)) {
|
|
varno_list = lappendi(varno_list, var->varno);
|
|
}
|
|
foreach (vi, var_list)
|
|
{
|
|
Var *in_list = (Var *)lfirst(vi);
|
|
|
|
if ( in_list->varno == var->varno &&
|
|
in_list->varattno == var->varattno )
|
|
break;
|
|
}
|
|
if ( vi == NIL )
|
|
var_list = lappend(var_list, var);
|
|
}
|
|
|
|
*relids = varno_list;
|
|
*vars = var_list;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* NumRelids--
|
|
* (formerly clause-relids)
|
|
*
|
|
* Returns the number of different relations referenced in 'clause'.
|
|
*/
|
|
int
|
|
NumRelids(Node *clause)
|
|
{
|
|
List *vars = pull_var_clause(clause);
|
|
List *i = NIL;
|
|
List *var_list = NIL;
|
|
|
|
foreach (i, vars) {
|
|
Var *var = (Var *)lfirst(i);
|
|
|
|
if (!intMember(var->varno, var_list)) {
|
|
var_list = lconsi(var->varno, var_list);
|
|
}
|
|
}
|
|
|
|
return(length(var_list));
|
|
}
|
|
|
|
/*
|
|
* contains-not--
|
|
*
|
|
* Returns t iff the clause is a 'not' clause or if any of the
|
|
* subclauses within an 'or' clause contain 'not's.
|
|
*
|
|
*/
|
|
bool
|
|
contains_not(Node *clause)
|
|
{
|
|
if (single_node(clause))
|
|
return (false);
|
|
|
|
if (not_clause(clause))
|
|
return (true);
|
|
|
|
if (or_clause(clause)) {
|
|
List *a;
|
|
foreach(a, ((Expr*)clause)->args) {
|
|
if (contains_not(lfirst(a)))
|
|
return (true);
|
|
}
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
|
|
/*
|
|
* join-clause-p--
|
|
*
|
|
* Returns t iff 'clause' is a valid join clause.
|
|
*
|
|
*/
|
|
bool
|
|
join_clause_p(Node *clause)
|
|
{
|
|
Node *leftop, *rightop;
|
|
|
|
if (!is_opclause(clause))
|
|
return false;
|
|
|
|
leftop = (Node*)get_leftop((Expr*)clause);
|
|
rightop = (Node*)get_rightop((Expr*)clause);
|
|
|
|
/*
|
|
* One side of the clause (i.e. left or right operands)
|
|
* must either be a var node ...
|
|
*/
|
|
if (IsA(leftop,Var) || IsA(rightop,Var))
|
|
return true;
|
|
|
|
/*
|
|
* ... or a func node.
|
|
*/
|
|
if (is_funcclause(leftop) || is_funcclause(rightop))
|
|
return(true);
|
|
|
|
return(false);
|
|
}
|
|
|
|
/*
|
|
* qual-clause-p--
|
|
*
|
|
* Returns t iff 'clause' is a valid qualification clause.
|
|
*
|
|
*/
|
|
bool
|
|
qual_clause_p(Node *clause)
|
|
{
|
|
if (!is_opclause(clause))
|
|
return false;
|
|
|
|
if (IsA (get_leftop((Expr*)clause),Var) &&
|
|
IsA (get_rightop((Expr*)clause),Const))
|
|
{
|
|
return(true);
|
|
}
|
|
else if (IsA (get_rightop((Expr*)clause),Var) &&
|
|
IsA (get_leftop((Expr*)clause),Const))
|
|
{
|
|
return(true);
|
|
}
|
|
return(false);
|
|
}
|
|
|
|
/*
|
|
* fix-opid--
|
|
* Calculate the opfid from the opno...
|
|
*
|
|
* Returns nothing.
|
|
*
|
|
*/
|
|
void
|
|
fix_opid(Node *clause)
|
|
{
|
|
if (clause==NULL || single_node(clause)) {
|
|
;
|
|
}
|
|
else if (or_clause (clause)) {
|
|
fix_opids(((Expr*)clause)->args);
|
|
}
|
|
else if (is_funcclause (clause)) {
|
|
fix_opids(((Expr*)clause)->args);
|
|
}
|
|
else if (IsA(clause,ArrayRef)) {
|
|
ArrayRef *aref = (ArrayRef *)clause;
|
|
|
|
fix_opids(aref->refupperindexpr);
|
|
fix_opids(aref->reflowerindexpr);
|
|
fix_opid(aref->refexpr);
|
|
fix_opid(aref->refassgnexpr);
|
|
}
|
|
else if (not_clause(clause)) {
|
|
fix_opid((Node*)get_notclausearg((Expr*)clause));
|
|
}
|
|
else if (is_opclause (clause)) {
|
|
replace_opid((Oper*)((Expr*)clause)->oper);
|
|
fix_opid((Node*)get_leftop((Expr*)clause));
|
|
fix_opid((Node*)get_rightop((Expr*)clause));
|
|
}
|
|
else if (agg_clause (clause)) {
|
|
fix_opid (((Aggreg*)clause)->target);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* fix-opids--
|
|
* Calculate the opfid from the opno for all the clauses...
|
|
*
|
|
* Returns its argument.
|
|
*
|
|
*/
|
|
List *
|
|
fix_opids(List *clauses)
|
|
{
|
|
List *clause;
|
|
|
|
foreach(clause, clauses)
|
|
fix_opid(lfirst(clause));
|
|
|
|
return(clauses);
|
|
}
|
|
|
|
/*
|
|
* get_relattval--
|
|
* For a non-join clause, returns a list consisting of the
|
|
* relid,
|
|
* attno,
|
|
* value of the CONST node (if any), and a
|
|
* flag indicating whether the value appears on the left or right
|
|
* of the operator and whether the value varied.
|
|
*
|
|
* OLD OBSOLETE COMMENT FOLLOWS:
|
|
* If 'clause' is not of the format (op var node) or (op node var),
|
|
* or if the var refers to a nested attribute, then -1's are returned for
|
|
* everything but the value a blank string "" (pointer to \0) is
|
|
* returned for the value if it is unknown or null.
|
|
* END OF OLD OBSOLETE COMMENT.
|
|
* NEW COMMENT:
|
|
* when defining rules one of the attibutes of the operator can
|
|
* be a Param node (which is supposed to be treated as a constant).
|
|
* However as there is no value specified for a parameter until run time
|
|
* this routine used to return "" as value, which made 'compute_selec'
|
|
* to bomb (because it was expecting a lisp integer and got back a lisp
|
|
* string). Now the code returns a plain old good "lispInteger(0)".
|
|
*
|
|
*/
|
|
void
|
|
get_relattval(Node *clause,
|
|
int *relid,
|
|
AttrNumber *attno,
|
|
Datum *constval,
|
|
int *flag)
|
|
{
|
|
Var *left = get_leftop((Expr*)clause);
|
|
Var *right = get_rightop((Expr*)clause);
|
|
|
|
if(is_opclause(clause) && IsA(left,Var) &&
|
|
IsA(right,Const)) {
|
|
|
|
if(right!=NULL) {
|
|
|
|
*relid = left->varno;
|
|
*attno = left->varattno;
|
|
*constval = ((Const *)right)->constvalue;
|
|
*flag = (_SELEC_CONSTANT_RIGHT_ | _SELEC_IS_CONSTANT_);
|
|
|
|
} else {
|
|
|
|
*relid = left->varno;
|
|
*attno = left->varattno;
|
|
*constval = 0;
|
|
*flag = (_SELEC_CONSTANT_RIGHT_ | _SELEC_NOT_CONSTANT_);
|
|
|
|
}
|
|
#ifdef INDEXSCAN_PATCH
|
|
} else if (is_opclause(clause) && IsA(left,Var) && IsA(right,Param)) {
|
|
/* Function parameter used as index scan arg. DZ - 27-8-1996 */
|
|
*relid = left->varno;
|
|
*attno = left->varattno;
|
|
*constval = 0;
|
|
*flag = (_SELEC_NOT_CONSTANT_);
|
|
#endif
|
|
}else if (is_opclause(clause) &&
|
|
is_funcclause((Node*)left) &&
|
|
IsA(right,Const)) {
|
|
List *args = ((Expr*)left)->args;
|
|
|
|
|
|
*relid = ((Var*)lfirst(args))->varno;
|
|
*attno = InvalidAttrNumber;
|
|
*constval = ((Const*)right)->constvalue;
|
|
*flag = (_SELEC_CONSTANT_RIGHT_ | _SELEC_IS_CONSTANT_);
|
|
|
|
/*
|
|
* XXX both of these func clause handling if's seem wrong to me.
|
|
* they assume that the first argument is the Var. It could
|
|
* not handle (for example) f(1, emp.name). I think I may have
|
|
* been assuming no constants in functional index scans when I
|
|
* implemented this originally (still currently true).
|
|
* -mer 10 Aug 1992
|
|
*/
|
|
} else if (is_opclause(clause) &&
|
|
is_funcclause((Node*)right) &&
|
|
IsA(left,Const)) {
|
|
List *args = ((Expr*)right)->args;
|
|
|
|
*relid = ((Var*)lfirst(args))->varno;
|
|
*attno = InvalidAttrNumber;
|
|
*constval = ((Const*)left)->constvalue;
|
|
*flag = ( _SELEC_IS_CONSTANT_);
|
|
|
|
} else if (is_opclause (clause) && IsA (right,Var) &&
|
|
IsA (left,Const)) {
|
|
if (left!=NULL) {
|
|
|
|
*relid = right->varno;
|
|
*attno = right->varattno;
|
|
*constval = ((Const*)left)->constvalue;
|
|
*flag = (_SELEC_IS_CONSTANT_);
|
|
} else {
|
|
|
|
*relid = right->varno;
|
|
*attno = right->varattno;
|
|
*constval = 0;
|
|
*flag = (_SELEC_NOT_CONSTANT_);
|
|
}
|
|
} else {
|
|
/* One or more of the operands are expressions
|
|
* (e.g., oper clauses)
|
|
*/
|
|
*relid = _SELEC_VALUE_UNKNOWN_;
|
|
*attno = _SELEC_VALUE_UNKNOWN_;
|
|
*constval = 0;
|
|
*flag = (_SELEC_NOT_CONSTANT_);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* get_relsatts--
|
|
*
|
|
* Returns a list
|
|
* ( relid1 attno1 relid2 attno2 )
|
|
* for a joinclause.
|
|
*
|
|
* If the clause is not of the form (op var var) or if any of the vars
|
|
* refer to nested attributes, then -1's are returned.
|
|
*
|
|
*/
|
|
void
|
|
get_rels_atts(Node *clause,
|
|
int *relid1,
|
|
AttrNumber *attno1,
|
|
int *relid2,
|
|
AttrNumber *attno2)
|
|
{
|
|
Var *left = get_leftop((Expr*)clause);
|
|
Var *right = get_rightop((Expr*)clause);
|
|
bool var_left = (IsA(left,Var));
|
|
bool var_right = (IsA(right,Var));
|
|
bool varexpr_left = (bool)((IsA(left,Func) || IsA (left,Oper)) &&
|
|
contain_var_clause((Node*)left));
|
|
bool varexpr_right = (bool)(( IsA(right,Func) || IsA (right,Oper)) &&
|
|
contain_var_clause((Node*)right));
|
|
|
|
if (is_opclause(clause)) {
|
|
if(var_left && var_right) {
|
|
|
|
*relid1 = left->varno;
|
|
*attno1 = left->varoattno;
|
|
*relid2 = right->varno;
|
|
*attno2 = right->varoattno;
|
|
return;
|
|
} else if (var_left && varexpr_right ) {
|
|
|
|
*relid1 = left->varno;
|
|
*attno1 = left->varoattno;
|
|
*relid2 = _SELEC_VALUE_UNKNOWN_;
|
|
*attno2 = _SELEC_VALUE_UNKNOWN_;
|
|
return;
|
|
} else if (varexpr_left && var_right) {
|
|
|
|
*relid1 = _SELEC_VALUE_UNKNOWN_;
|
|
*attno1 = _SELEC_VALUE_UNKNOWN_;
|
|
*relid2 = right->varno;
|
|
*attno2 = right->varoattno;
|
|
return;
|
|
}
|
|
}
|
|
|
|
*relid1 = _SELEC_VALUE_UNKNOWN_;
|
|
*attno1 = _SELEC_VALUE_UNKNOWN_;
|
|
*relid2 = _SELEC_VALUE_UNKNOWN_;
|
|
*attno2 = _SELEC_VALUE_UNKNOWN_;
|
|
return;
|
|
}
|
|
|
|
void
|
|
CommuteClause(Node *clause)
|
|
{
|
|
Node *temp;
|
|
Oper *commu;
|
|
OperatorTupleForm commuTup;
|
|
HeapTuple heapTup;
|
|
|
|
if (!is_opclause(clause))
|
|
return;
|
|
|
|
heapTup = (HeapTuple)
|
|
get_operator_tuple(get_commutator(((Oper*)((Expr*)clause)->oper)->opno));
|
|
|
|
if (heapTup == (HeapTuple)NULL)
|
|
return;
|
|
|
|
commuTup = (OperatorTupleForm)GETSTRUCT(heapTup);
|
|
|
|
commu = makeOper(heapTup->t_oid,
|
|
InvalidOid,
|
|
commuTup->oprresult,
|
|
((Oper*)((Expr*)clause)->oper)->opsize,
|
|
NULL);
|
|
|
|
/*
|
|
* reform the clause -> (operator func/var constant)
|
|
*/
|
|
((Expr*)clause)->oper = (Node*)commu;
|
|
temp = lfirst(((Expr*)clause)->args);
|
|
lfirst(((Expr*)clause)->args) = lsecond(((Expr*)clause)->args);
|
|
lsecond(((Expr*)clause)->args) = temp;
|
|
}
|
|
|
|
|