postgresql/src/backend/nodes/equalfuncs.c

265 lines
5.9 KiB
C

/*-------------------------------------------------------------------------
*
* equalfuncs.c
* Equality functions to compare node trees.
*
* NOTE: it is intentional that parse location fields (in nodes that have
* one) are not compared. This is because we want, for example, a variable
* "x" to be considered equal() to another reference to "x" in the query.
*
*
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/backend/nodes/equalfuncs.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "miscadmin.h"
#include "utils/datum.h"
/*
* Macros to simplify comparison of different kinds of fields. Use these
* wherever possible to reduce the chance for silly typos. Note that these
* hard-wire the convention that the local variables in an Equal routine are
* named 'a' and 'b'.
*/
/* Compare a simple scalar field (int, float, bool, enum, etc) */
#define COMPARE_SCALAR_FIELD(fldname) \
do { \
if (a->fldname != b->fldname) \
return false; \
} while (0)
/* Compare a field that is a pointer to some kind of Node or Node tree */
#define COMPARE_NODE_FIELD(fldname) \
do { \
if (!equal(a->fldname, b->fldname)) \
return false; \
} while (0)
/* Compare a field that is a pointer to a Bitmapset */
#define COMPARE_BITMAPSET_FIELD(fldname) \
do { \
if (!bms_equal(a->fldname, b->fldname)) \
return false; \
} while (0)
/* Compare a field that is a pointer to a C string, or perhaps NULL */
#define COMPARE_STRING_FIELD(fldname) \
do { \
if (!equalstr(a->fldname, b->fldname)) \
return false; \
} while (0)
/* Macro for comparing string fields that might be NULL */
#define equalstr(a, b) \
(((a) != NULL && (b) != NULL) ? (strcmp(a, b) == 0) : (a) == (b))
/* Compare a field that is an inline array */
#define COMPARE_ARRAY_FIELD(fldname) \
do { \
if (memcmp(a->fldname, b->fldname, sizeof(a->fldname)) != 0) \
return false; \
} while (0)
/* Compare a field that is a pointer to a simple palloc'd object of size sz */
#define COMPARE_POINTER_FIELD(fldname, sz) \
do { \
if (memcmp(a->fldname, b->fldname, (sz)) != 0) \
return false; \
} while (0)
/* Compare a parse location field (this is a no-op, per note above) */
#define COMPARE_LOCATION_FIELD(fldname) \
((void) 0)
/* Compare a CoercionForm field (also a no-op, per comment in primnodes.h) */
#define COMPARE_COERCIONFORM_FIELD(fldname) \
((void) 0)
#include "equalfuncs.funcs.c"
/*
* Support functions for nodes with custom_copy_equal attribute
*/
static bool
_equalConst(const Const *a, const Const *b)
{
COMPARE_SCALAR_FIELD(consttype);
COMPARE_SCALAR_FIELD(consttypmod);
COMPARE_SCALAR_FIELD(constcollid);
COMPARE_SCALAR_FIELD(constlen);
COMPARE_SCALAR_FIELD(constisnull);
COMPARE_SCALAR_FIELD(constbyval);
COMPARE_LOCATION_FIELD(location);
/*
* 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->constbyval, a->constlen);
}
static bool
_equalExtensibleNode(const ExtensibleNode *a, const ExtensibleNode *b)
{
const ExtensibleNodeMethods *methods;
COMPARE_STRING_FIELD(extnodename);
/* At this point, we know extnodename is the same for both nodes. */
methods = GetExtensibleNodeMethods(a->extnodename, false);
/* compare the private fields */
if (!methods->nodeEqual(a, b))
return false;
return true;
}
static bool
_equalA_Const(const A_Const *a, const A_Const *b)
{
COMPARE_SCALAR_FIELD(isnull);
/* Hack for in-line val field. Also val is not valid if isnull is true */
if (!a->isnull &&
!equal(&a->val, &b->val))
return false;
COMPARE_LOCATION_FIELD(location);
return true;
}
static bool
_equalBitmapset(const Bitmapset *a, const Bitmapset *b)
{
return bms_equal(a, b);
}
/*
* Lists are handled specially
*/
static bool
_equalList(const List *a, const List *b)
{
const ListCell *item_a;
const ListCell *item_b;
/*
* Try to reject by simple scalar checks before grovelling through all the
* list elements...
*/
COMPARE_SCALAR_FIELD(type);
COMPARE_SCALAR_FIELD(length);
/*
* We place the switch outside the loop for the sake of efficiency; this
* may not be worth doing...
*/
switch (a->type)
{
case T_List:
forboth(item_a, a, item_b, b)
{
if (!equal(lfirst(item_a), lfirst(item_b)))
return false;
}
break;
case T_IntList:
forboth(item_a, a, item_b, b)
{
if (lfirst_int(item_a) != lfirst_int(item_b))
return false;
}
break;
case T_OidList:
forboth(item_a, a, item_b, b)
{
if (lfirst_oid(item_a) != lfirst_oid(item_b))
return false;
}
break;
case T_XidList:
forboth(item_a, a, item_b, b)
{
if (lfirst_xid(item_a) != lfirst_xid(item_b))
return false;
}
break;
default:
elog(ERROR, "unrecognized list node type: %d",
(int) a->type);
return false; /* keep compiler quiet */
}
/*
* If we got here, we should have run out of elements of both lists
*/
Assert(item_a == NULL);
Assert(item_b == NULL);
return true;
}
/*
* equal
* returns whether two nodes are equal
*/
bool
equal(const void *a, const void *b)
{
bool retval;
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;
/* Guard against stack overflow due to overly complex expressions */
check_stack_depth();
switch (nodeTag(a))
{
#include "equalfuncs.switch.c"
case T_List:
case T_IntList:
case T_OidList:
case T_XidList:
retval = _equalList(a, b);
break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(a));
retval = false; /* keep compiler quiet */
break;
}
return retval;
}