265 lines
5.9 KiB
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;
|
|
}
|