diff options
Diffstat (limited to 'contrib/ltree/ltree_op.c')
-rw-r--r-- | contrib/ltree/ltree_op.c | 69 |
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) { |