mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-31 03:57:21 +02:00
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc to all files copyright Regents of Berkeley. Man, that's a lot of files.
913 lines
18 KiB
C
913 lines
18 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* equalfuncs.c
|
|
* equality functions to compare node trees
|
|
*
|
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.56 2000/01/26 05:56:31 momjian Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "nodes/plannodes.h"
|
|
#include "nodes/relation.h"
|
|
#include "utils/datum.h"
|
|
|
|
static bool equali(List *a, List *b);
|
|
|
|
|
|
/*
|
|
* Stuff from primnodes.h
|
|
*/
|
|
|
|
static bool
|
|
_equalResdom(Resdom *a, Resdom *b)
|
|
{
|
|
if (a->resno != b->resno)
|
|
return false;
|
|
if (a->restype != b->restype)
|
|
return false;
|
|
if (a->restypmod != b->restypmod)
|
|
return false;
|
|
if (a->resname && b->resname)
|
|
{
|
|
if (strcmp(a->resname, b->resname) != 0)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
/* must both be null to be equal */
|
|
if (a->resname != b->resname)
|
|
return false;
|
|
}
|
|
if (a->ressortgroupref != b->ressortgroupref)
|
|
return false;
|
|
if (a->reskey != b->reskey)
|
|
return false;
|
|
if (a->reskeyop != b->reskeyop)
|
|
return false;
|
|
/* we ignore resjunk flag ... is this correct? */
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalFjoin(Fjoin *a, Fjoin *b)
|
|
{
|
|
int nNodes;
|
|
|
|
if (a->fj_initialized != b->fj_initialized)
|
|
return false;
|
|
if (a->fj_nNodes != b->fj_nNodes)
|
|
return false;
|
|
if (!equal(a->fj_innerNode, b->fj_innerNode))
|
|
return false;
|
|
|
|
nNodes = a->fj_nNodes;
|
|
if (memcmp(a->fj_results, b->fj_results, nNodes * sizeof(Datum)) != 0)
|
|
return false;
|
|
if (memcmp(a->fj_alwaysDone, b->fj_alwaysDone, nNodes * sizeof(bool)) != 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalExpr(Expr *a, Expr *b)
|
|
{
|
|
/* We do not examine typeOid, since the optimizer often doesn't
|
|
* bother to set it in created nodes, and it is logically a
|
|
* derivative of the oper field anyway.
|
|
*/
|
|
if (a->opType != b->opType)
|
|
return false;
|
|
if (!equal(a->oper, b->oper))
|
|
return false;
|
|
if (!equal(a->args, b->args))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalVar(Var *a, Var *b)
|
|
{
|
|
if (a->varno != b->varno)
|
|
return false;
|
|
if (a->varattno != b->varattno)
|
|
return false;
|
|
if (a->vartype != b->vartype)
|
|
return false;
|
|
if (a->vartypmod != b->vartypmod)
|
|
return false;
|
|
if (a->varlevelsup != b->varlevelsup)
|
|
return false;
|
|
if (a->varnoold != b->varnoold)
|
|
return false;
|
|
if (a->varoattno != b->varoattno)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalOper(Oper *a, Oper *b)
|
|
{
|
|
if (a->opno != b->opno)
|
|
return false;
|
|
if (a->opresulttype != b->opresulttype)
|
|
return false;
|
|
/* We do not examine opid, opsize, or op_fcache, since these are
|
|
* logically derived from opno, and they may not be set yet depending
|
|
* on how far along the node is in the parse/plan pipeline.
|
|
*
|
|
* It's probably not really necessary to check opresulttype either...
|
|
*/
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalConst(Const *a, Const *b)
|
|
{
|
|
if (a->consttype != b->consttype)
|
|
return false;
|
|
if (a->constlen != b->constlen)
|
|
return false;
|
|
if (a->constisnull != b->constisnull)
|
|
return false;
|
|
if (a->constbyval != b->constbyval)
|
|
return false;
|
|
/* XXX What about constisset and constiscast? */
|
|
/*
|
|
* We treat all NULL constants of the same type as equal.
|
|
* Someday this might need to change? But datumIsEqual
|
|
* doesn't work on nulls, so...
|
|
*/
|
|
if (a->constisnull)
|
|
return true;
|
|
return (datumIsEqual(a->constvalue, b->constvalue,
|
|
a->consttype, a->constbyval, a->constlen));
|
|
}
|
|
|
|
static bool
|
|
_equalParam(Param *a, Param *b)
|
|
{
|
|
if (a->paramkind != b->paramkind)
|
|
return false;
|
|
if (a->paramtype != b->paramtype)
|
|
return false;
|
|
if (!equal(a->param_tlist, b->param_tlist))
|
|
return false;
|
|
|
|
switch (a->paramkind)
|
|
{
|
|
case PARAM_NAMED:
|
|
case PARAM_NEW:
|
|
case PARAM_OLD:
|
|
if (strcmp(a->paramname, b->paramname) != 0)
|
|
return false;
|
|
break;
|
|
case PARAM_NUM:
|
|
case PARAM_EXEC:
|
|
if (a->paramid != b->paramid)
|
|
return false;
|
|
break;
|
|
case PARAM_INVALID:
|
|
|
|
/*
|
|
* XXX: Hmmm... What are we supposed to return in this case ??
|
|
*/
|
|
return true;
|
|
break;
|
|
default:
|
|
elog(ERROR, "_equalParam: Invalid paramkind value: %d",
|
|
a->paramkind);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalFunc(Func *a, Func *b)
|
|
{
|
|
if (a->funcid != b->funcid)
|
|
return false;
|
|
if (a->functype != b->functype)
|
|
return false;
|
|
if (a->funcisindex != b->funcisindex)
|
|
return false;
|
|
if (a->funcsize != b->funcsize)
|
|
return false;
|
|
/* Note we do not look at func_fcache */
|
|
if (!equal(a->func_tlist, b->func_tlist))
|
|
return false;
|
|
if (!equal(a->func_planlist, b->func_planlist))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalAggref(Aggref *a, Aggref *b)
|
|
{
|
|
if (strcmp(a->aggname, b->aggname) != 0)
|
|
return false;
|
|
if (a->basetype != b->basetype)
|
|
return false;
|
|
if (a->aggtype != b->aggtype)
|
|
return false;
|
|
if (!equal(a->target, b->target))
|
|
return false;
|
|
if (a->usenulls != b->usenulls)
|
|
return false;
|
|
if (a->aggstar != b->aggstar)
|
|
return false;
|
|
if (a->aggdistinct != b->aggdistinct)
|
|
return false;
|
|
/* ignore aggno, which is only a private field for the executor */
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalSubLink(SubLink *a, SubLink *b)
|
|
{
|
|
if (a->subLinkType != b->subLinkType)
|
|
return false;
|
|
if (a->useor != b->useor)
|
|
return false;
|
|
if (!equal(a->lefthand, b->lefthand))
|
|
return false;
|
|
if (!equal(a->oper, b->oper))
|
|
return false;
|
|
if (!equal(a->subselect, b->subselect))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalArray(Array *a, Array *b)
|
|
{
|
|
if (a->arrayelemtype != b->arrayelemtype)
|
|
return false;
|
|
/* We need not check arrayelemlength, arrayelembyval if types match */
|
|
if (a->arrayndim != b->arrayndim)
|
|
return false;
|
|
/* XXX shouldn't we be checking all indices??? */
|
|
if (a->arraylow.indx[0] != b->arraylow.indx[0])
|
|
return false;
|
|
if (a->arrayhigh.indx[0] != b->arrayhigh.indx[0])
|
|
return false;
|
|
if (a->arraylen != b->arraylen)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalArrayRef(ArrayRef *a, ArrayRef *b)
|
|
{
|
|
if (a->refelemtype != b->refelemtype)
|
|
return false;
|
|
if (a->refattrlength != b->refattrlength)
|
|
return false;
|
|
if (a->refelemlength != b->refelemlength)
|
|
return false;
|
|
if (a->refelembyval != b->refelembyval)
|
|
return false;
|
|
if (!equal(a->refupperindexpr, b->refupperindexpr))
|
|
return false;
|
|
if (!equal(a->reflowerindexpr, b->reflowerindexpr))
|
|
return false;
|
|
if (!equal(a->refexpr, b->refexpr))
|
|
return false;
|
|
return equal(a->refassgnexpr, b->refassgnexpr);
|
|
}
|
|
|
|
/*
|
|
* Stuff from relation.h
|
|
*/
|
|
|
|
static bool
|
|
_equalRelOptInfo(RelOptInfo *a, RelOptInfo *b)
|
|
{
|
|
/* We treat RelOptInfos as equal if they refer to the same base rels
|
|
* joined in the same order. Is this sufficient?
|
|
*/
|
|
return equali(a->relids, b->relids);
|
|
}
|
|
|
|
static bool
|
|
_equalIndexOptInfo(IndexOptInfo *a, IndexOptInfo *b)
|
|
{
|
|
/* We treat IndexOptInfos as equal if they refer to the same index.
|
|
* Is this sufficient?
|
|
*/
|
|
if (a->indexoid != b->indexoid)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalPathKeyItem(PathKeyItem *a, PathKeyItem *b)
|
|
{
|
|
if (a->sortop != b->sortop)
|
|
return false;
|
|
if (!equal(a->key, b->key))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalPath(Path *a, Path *b)
|
|
{
|
|
if (a->pathtype != b->pathtype)
|
|
return false;
|
|
if (!equal(a->parent, b->parent))
|
|
return false;
|
|
/* do not check path_cost, since it may not be set yet, and being
|
|
* a float there are roundoff error issues anyway...
|
|
*/
|
|
if (!equal(a->pathkeys, b->pathkeys))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalIndexPath(IndexPath *a, IndexPath *b)
|
|
{
|
|
if (!_equalPath((Path *) a, (Path *) b))
|
|
return false;
|
|
if (!equali(a->indexid, b->indexid))
|
|
return false;
|
|
if (!equal(a->indexqual, b->indexqual))
|
|
return false;
|
|
if (!equali(a->joinrelids, b->joinrelids))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalTidPath(TidPath *a, TidPath *b)
|
|
{
|
|
if (!_equalPath((Path *) a, (Path *) b))
|
|
return false;
|
|
if (!equal(a->tideval, b->tideval))
|
|
return false;
|
|
if (!equali(a->unjoined_relids, b->unjoined_relids))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalJoinPath(JoinPath *a, JoinPath *b)
|
|
{
|
|
if (!_equalPath((Path *) a, (Path *) b))
|
|
return false;
|
|
if (!equal(a->outerjoinpath, b->outerjoinpath))
|
|
return false;
|
|
if (!equal(a->innerjoinpath, b->innerjoinpath))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalNestPath(NestPath *a, NestPath *b)
|
|
{
|
|
if (!_equalJoinPath((JoinPath *) a, (JoinPath *) b))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalMergePath(MergePath *a, MergePath *b)
|
|
{
|
|
if (!_equalJoinPath((JoinPath *) a, (JoinPath *) b))
|
|
return false;
|
|
if (!equal(a->path_mergeclauses, b->path_mergeclauses))
|
|
return false;
|
|
if (!equal(a->outersortkeys, b->outersortkeys))
|
|
return false;
|
|
if (!equal(a->innersortkeys, b->innersortkeys))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalHashPath(HashPath *a, HashPath *b)
|
|
{
|
|
if (!_equalJoinPath((JoinPath *) a, (JoinPath *) b))
|
|
return false;
|
|
if (!equal(a->path_hashclauses, b->path_hashclauses))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/* XXX This equality function is a quick hack, should be
|
|
* fixed to compare all fields.
|
|
*
|
|
* XXX Why is this even here? We don't have equal() funcs for
|
|
* any other kinds of Plan nodes... likely this is dead code...
|
|
*/
|
|
static bool
|
|
_equalIndexScan(IndexScan *a, IndexScan *b)
|
|
{
|
|
/*
|
|
* if(a->scan.plan.cost != b->scan.plan.cost) return(false);
|
|
*/
|
|
|
|
if (!equal(a->indxqual, b->indxqual))
|
|
return false;
|
|
|
|
if (a->scan.scanrelid != b->scan.scanrelid)
|
|
return false;
|
|
|
|
if (a->indxorderdir != b->indxorderdir)
|
|
return false;
|
|
|
|
if (!equali(a->indxid, b->indxid))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalTidScan(TidScan *a, TidScan *b)
|
|
{
|
|
Assert(IsA(a, TidScan));
|
|
Assert(IsA(b, TidScan));
|
|
|
|
/*
|
|
* if(a->scan.plan.cost != b->scan.plan.cost) return(false);
|
|
*/
|
|
|
|
if (a->needRescan != b->needRescan)
|
|
return false;
|
|
|
|
if (!equal(a->tideval, b->tideval))
|
|
return false;
|
|
|
|
if (a->scan.scanrelid != b->scan.scanrelid)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalSubPlan(SubPlan *a, SubPlan *b)
|
|
{
|
|
/* should compare plans, but have to settle for comparing plan IDs */
|
|
if (a->plan_id != b->plan_id)
|
|
return false;
|
|
|
|
if (!equal(a->rtable, b->rtable))
|
|
return false;
|
|
|
|
if (!equal(a->sublink, b->sublink))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
|
|
{
|
|
if (!equal(a->clause, b->clause))
|
|
return false;
|
|
if (!equal(a->subclauseindices, b->subclauseindices))
|
|
return false;
|
|
if (a->mergejoinoperator != b->mergejoinoperator)
|
|
return false;
|
|
if (a->left_sortop != b->left_sortop)
|
|
return false;
|
|
if (a->right_sortop != b->right_sortop)
|
|
return false;
|
|
if (a->hashjoinoperator != b->hashjoinoperator)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalJoinInfo(JoinInfo *a, JoinInfo *b)
|
|
{
|
|
if (!equali(a->unjoined_relids, b->unjoined_relids))
|
|
return false;
|
|
if (!equal(a->jinfo_restrictinfo, b->jinfo_restrictinfo))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalIter(Iter *a, Iter *b)
|
|
{
|
|
return equal(a->iterexpr, b->iterexpr);
|
|
}
|
|
|
|
static bool
|
|
_equalStream(Stream *a, Stream *b)
|
|
{
|
|
if (a->clausetype != b->clausetype)
|
|
return false;
|
|
if (a->groupup != b->groupup)
|
|
return false;
|
|
if (a->groupcost != b->groupcost)
|
|
return false;
|
|
if (a->groupsel != b->groupsel)
|
|
return false;
|
|
if (!equal(a->pathptr, b->pathptr))
|
|
return false;
|
|
if (!equal(a->cinfo, b->cinfo))
|
|
return false;
|
|
if (!equal(a->upstream, b->upstream))
|
|
return false;
|
|
return equal(a->downstream, b->downstream);
|
|
}
|
|
|
|
/*
|
|
* Stuff from execnodes.h
|
|
*/
|
|
|
|
/*
|
|
* EState is a subclass of Node.
|
|
*/
|
|
static bool
|
|
_equalEState(EState *a, EState *b)
|
|
{
|
|
if (a->es_direction != b->es_direction)
|
|
return false;
|
|
|
|
if (!equal(a->es_range_table, b->es_range_table))
|
|
return false;
|
|
|
|
if (a->es_result_relation_info != b->es_result_relation_info)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Stuff from parsenodes.h
|
|
*/
|
|
|
|
static bool
|
|
_equalQuery(Query *a, Query *b)
|
|
{
|
|
if (a->commandType != b->commandType)
|
|
return false;
|
|
if (!equal(a->utilityStmt, b->utilityStmt))
|
|
return false;
|
|
if (a->resultRelation != b->resultRelation)
|
|
return false;
|
|
if (a->into && b->into)
|
|
{
|
|
if (strcmp(a->into, b->into) != 0)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (a->into != b->into)
|
|
return false;
|
|
}
|
|
if (a->isPortal != b->isPortal)
|
|
return false;
|
|
if (a->isBinary != b->isBinary)
|
|
return false;
|
|
if (a->isTemp != b->isTemp)
|
|
return false;
|
|
if (a->unionall != b->unionall)
|
|
return false;
|
|
if (a->hasAggs != b->hasAggs)
|
|
return false;
|
|
if (a->hasSubLinks != b->hasSubLinks)
|
|
return false;
|
|
if (!equal(a->rtable, b->rtable))
|
|
return false;
|
|
if (!equal(a->targetList, b->targetList))
|
|
return false;
|
|
if (!equal(a->qual, b->qual))
|
|
return false;
|
|
if (!equal(a->rowMark, b->rowMark))
|
|
return false;
|
|
if (a->uniqueFlag && b->uniqueFlag)
|
|
{
|
|
if (strcmp(a->uniqueFlag, b->uniqueFlag) != 0)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (a->uniqueFlag != b->uniqueFlag)
|
|
return false;
|
|
}
|
|
if (!equal(a->sortClause, b->sortClause))
|
|
return false;
|
|
if (!equal(a->groupClause, b->groupClause))
|
|
return false;
|
|
if (!equal(a->havingQual, b->havingQual))
|
|
return false;
|
|
if (!equal(a->intersectClause, b->intersectClause))
|
|
return false;
|
|
if (!equal(a->unionClause, b->unionClause))
|
|
return false;
|
|
if (!equal(a->limitOffset, b->limitOffset))
|
|
return false;
|
|
if (!equal(a->limitCount, b->limitCount))
|
|
return false;
|
|
|
|
/*
|
|
* We do not check the internal-to-the-planner fields: base_rel_list,
|
|
* join_rel_list, query_pathkeys. They might not be set yet, and
|
|
* in any case they should be derivable from the other fields.
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
|
|
{
|
|
if (a->relname && b->relname)
|
|
{
|
|
if (strcmp(a->relname, b->relname) != 0)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (a->relname != b->relname)
|
|
return false;
|
|
}
|
|
if (a->refname && b->refname)
|
|
{
|
|
if (strcmp(a->refname, b->refname) != 0)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (a->refname != b->refname)
|
|
return false;
|
|
}
|
|
if (a->relid != b->relid)
|
|
return false;
|
|
if (a->inh != b->inh)
|
|
return false;
|
|
if (a->inFromCl != b->inFromCl)
|
|
return false;
|
|
if (a->inJoinSet != b->inJoinSet)
|
|
return false;
|
|
if (a->skipAcl != b->skipAcl)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalTargetEntry(TargetEntry *a, TargetEntry *b)
|
|
{
|
|
if (!equal(a->resdom, b->resdom))
|
|
return false;
|
|
if (!equal(a->fjoin, b->fjoin))
|
|
return false;
|
|
if (!equal(a->expr, b->expr))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalCaseExpr(CaseExpr *a, CaseExpr *b)
|
|
{
|
|
if (a->casetype != b->casetype)
|
|
return false;
|
|
if (!equal(a->arg, b->arg))
|
|
return false;
|
|
if (!equal(a->args, b->args))
|
|
return false;
|
|
if (!equal(a->defresult, b->defresult))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_equalCaseWhen(CaseWhen *a, CaseWhen *b)
|
|
{
|
|
if (!equal(a->expr, b->expr))
|
|
return false;
|
|
if (!equal(a->result, b->result))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Stuff from pg_list.h
|
|
*/
|
|
|
|
static bool
|
|
_equalValue(Value *a, Value *b)
|
|
{
|
|
if (a->type != b->type)
|
|
return false;
|
|
|
|
switch (a->type)
|
|
{
|
|
case T_String:
|
|
return strcmp(a->val.str, b->val.str);
|
|
case T_Integer:
|
|
return a->val.ival == b->val.ival;
|
|
case T_Float:
|
|
return a->val.dval == b->val.dval;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* equal
|
|
* returns whether two nodes are equal
|
|
*/
|
|
bool
|
|
equal(void *a, void *b)
|
|
{
|
|
bool retval = false;
|
|
|
|
if (a == b)
|
|
return true;
|
|
|
|
/*
|
|
* note that a!=b, so only one of them can be NULL
|
|
*/
|
|
if (a == NULL || b == NULL)
|
|
return false;
|
|
|
|
/*
|
|
* are they the same type of nodes?
|
|
*/
|
|
if (nodeTag(a) != nodeTag(b))
|
|
return false;
|
|
|
|
switch (nodeTag(a))
|
|
{
|
|
case T_Resdom:
|
|
retval = _equalResdom(a, b);
|
|
break;
|
|
case T_Fjoin:
|
|
retval = _equalFjoin(a, b);
|
|
break;
|
|
case T_Expr:
|
|
retval = _equalExpr(a, b);
|
|
break;
|
|
case T_Iter:
|
|
retval = _equalIter(a, b);
|
|
break;
|
|
case T_Stream:
|
|
retval = _equalStream(a, b);
|
|
break;
|
|
case T_Var:
|
|
retval = _equalVar(a, b);
|
|
break;
|
|
case T_Array:
|
|
retval = _equalArray(a, b);
|
|
break;
|
|
case T_ArrayRef:
|
|
retval = _equalArrayRef(a, b);
|
|
break;
|
|
case T_Oper:
|
|
retval = _equalOper(a, b);
|
|
break;
|
|
case T_Const:
|
|
retval = _equalConst(a, b);
|
|
break;
|
|
case T_Param:
|
|
retval = _equalParam(a, b);
|
|
break;
|
|
case T_Aggref:
|
|
retval = _equalAggref(a, b);
|
|
break;
|
|
case T_SubLink:
|
|
retval = _equalSubLink(a, b);
|
|
break;
|
|
case T_Func:
|
|
retval = _equalFunc(a, b);
|
|
break;
|
|
case T_RestrictInfo:
|
|
retval = _equalRestrictInfo(a, b);
|
|
break;
|
|
case T_RelOptInfo:
|
|
retval = _equalRelOptInfo(a, b);
|
|
break;
|
|
case T_IndexOptInfo:
|
|
retval = _equalIndexOptInfo(a, b);
|
|
break;
|
|
case T_PathKeyItem:
|
|
retval = _equalPathKeyItem(a, b);
|
|
break;
|
|
case T_Path:
|
|
retval = _equalPath(a, b);
|
|
break;
|
|
case T_IndexPath:
|
|
retval = _equalIndexPath(a, b);
|
|
break;
|
|
case T_TidPath:
|
|
retval = _equalTidPath(a, b);
|
|
break;
|
|
case T_NestPath:
|
|
retval = _equalNestPath(a, b);
|
|
break;
|
|
case T_MergePath:
|
|
retval = _equalMergePath(a, b);
|
|
break;
|
|
case T_HashPath:
|
|
retval = _equalHashPath(a, b);
|
|
break;
|
|
case T_IndexScan:
|
|
retval = _equalIndexScan(a, b);
|
|
break;
|
|
case T_TidScan:
|
|
retval = _equalTidScan(a, b);
|
|
break;
|
|
case T_SubPlan:
|
|
retval = _equalSubPlan(a, b);
|
|
break;
|
|
case T_JoinInfo:
|
|
retval = _equalJoinInfo(a, b);
|
|
break;
|
|
case T_EState:
|
|
retval = _equalEState(a, b);
|
|
break;
|
|
case T_Integer:
|
|
case T_String:
|
|
case T_Float:
|
|
retval = _equalValue(a, b);
|
|
break;
|
|
case T_List:
|
|
{
|
|
List *la = (List *) a;
|
|
List *lb = (List *) b;
|
|
List *l;
|
|
|
|
/* Try to reject by length check before we grovel through
|
|
* all the elements...
|
|
*/
|
|
if (length(la) != length(lb))
|
|
return false;
|
|
foreach(l, la)
|
|
{
|
|
if (!equal(lfirst(l), lfirst(lb)))
|
|
return false;
|
|
lb = lnext(lb);
|
|
}
|
|
retval = true;
|
|
}
|
|
break;
|
|
case T_Query:
|
|
retval = _equalQuery(a, b);
|
|
break;
|
|
case T_RangeTblEntry:
|
|
retval = _equalRangeTblEntry(a, b);
|
|
break;
|
|
case T_TargetEntry:
|
|
retval = _equalTargetEntry(a, b);
|
|
break;
|
|
case T_CaseExpr:
|
|
retval = _equalCaseExpr(a, b);
|
|
break;
|
|
case T_CaseWhen:
|
|
retval = _equalCaseWhen(a, b);
|
|
break;
|
|
default:
|
|
elog(NOTICE, "equal: don't know whether nodes of type %d are equal",
|
|
nodeTag(a));
|
|
break;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* equali
|
|
* compares two lists of integers
|
|
*/
|
|
static bool
|
|
equali(List *a, List *b)
|
|
{
|
|
List *l;
|
|
|
|
foreach(l, a)
|
|
{
|
|
if (b == NIL)
|
|
return false;
|
|
if (lfirsti(l) != lfirsti(b))
|
|
return false;
|
|
b = lnext(b);
|
|
}
|
|
if (b != NIL)
|
|
return false;
|
|
return true;
|
|
}
|