summaryrefslogtreecommitdiff
path: root/contrib/ltree/ltree_op.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ltree/ltree_op.c')
-rw-r--r--contrib/ltree/ltree_op.c69
1 files changed, 69 insertions, 0 deletions
diff --git a/contrib/ltree/ltree_op.c b/contrib/ltree/ltree_op.c
index da1db5fcd22..24a21d3ea01 100644
--- a/contrib/ltree/ltree_op.c
+++ b/contrib/ltree/ltree_op.c
@@ -9,6 +9,7 @@
#include "access/htup_details.h"
#include "catalog/pg_statistic.h"
+#include "common/hashfn.h"
#include "ltree.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
@@ -24,6 +25,8 @@ PG_FUNCTION_INFO_V1(ltree_eq);
PG_FUNCTION_INFO_V1(ltree_ne);
PG_FUNCTION_INFO_V1(ltree_ge);
PG_FUNCTION_INFO_V1(ltree_gt);
+PG_FUNCTION_INFO_V1(hash_ltree);
+PG_FUNCTION_INFO_V1(hash_ltree_extended);
PG_FUNCTION_INFO_V1(nlevel);
PG_FUNCTION_INFO_V1(ltree_isparent);
PG_FUNCTION_INFO_V1(ltree_risparent);
@@ -129,6 +132,72 @@ ltree_ne(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(res != 0);
}
+/* Compute a hash for the ltree */
+Datum
+hash_ltree(PG_FUNCTION_ARGS)
+{
+ ltree *a = PG_GETARG_LTREE_P(0);
+ uint32 result = 1;
+ int an = a->numlevel;
+ ltree_level *al = LTREE_FIRST(a);
+
+ while (an > 0)
+ {
+ uint32 levelHash = DatumGetUInt32(hash_any((unsigned char *) al->name, al->len));
+
+ /*
+ * Combine hash values of successive elements by multiplying the
+ * current value by 31 and adding on the new element's hash value.
+ *
+ * This method is borrowed from hash_array(), which see for further
+ * commentary.
+ */
+ result = (result << 5) - result + levelHash;
+
+ an--;
+ al = LEVEL_NEXT(al);
+ }
+
+ PG_FREE_IF_COPY(a, 0);
+ PG_RETURN_UINT32(result);
+}
+
+/* Compute an extended hash for the ltree */
+Datum
+hash_ltree_extended(PG_FUNCTION_ARGS)
+{
+ ltree *a = PG_GETARG_LTREE_P(0);
+ const uint64 seed = PG_GETARG_INT64(1);
+ uint64 result = 1;
+ int an = a->numlevel;
+ ltree_level *al = LTREE_FIRST(a);
+
+ /*
+ * If the path has length zero, return 1 + seed to ensure that the low 32
+ * bits of the result match hash_ltree when the seed is 0, as required by
+ * the hash index support functions, but to also return a different value
+ * when there is a seed.
+ */
+ if (an == 0)
+ {
+ PG_FREE_IF_COPY(a, 0);
+ PG_RETURN_UINT64(result + seed);
+ }
+
+ while (an > 0)
+ {
+ uint64 levelHash = DatumGetUInt64(hash_any_extended((unsigned char *) al->name, al->len, seed));
+
+ result = (result << 5) - result + levelHash;
+
+ an--;
+ al = LEVEL_NEXT(al);
+ }
+
+ PG_FREE_IF_COPY(a, 0);
+ PG_RETURN_UINT64(result);
+}
+
Datum
nlevel(PG_FUNCTION_ARGS)
{