Class HashCanonicalValue

java.lang.Object
dev.frostlake.functions.scalar.hash.HashCanonicalValue

public final class HashCanonicalValue extends Object
The bytes HASH and HASH_AGG hash for one argument: a TYPE-CLASS tag followed by a canonical rendering of the value. The tag and the rendering together decide which arguments hash ALIKE, which is the only part of Snowflake's HASH that can be reproduced from outside — the 64-bit values themselves come from a proprietary algorithm and are deliberately not imitated.

The classes are live-verified, and two of them are counter-intuitive:

  • Numbers hash by VALUE, never by declared type or scale. 1, 1.00, 1.0000, 1::NUMBER(38,10) and 1.0::FLOAT are one hash; so are 2.5 and 2.50. Trailing zeros are stripped, and -0.0 renders as 0.
  • BOOLEAN is in the NUMBER class — TRUE hashes bit-identically to 1 and FALSE to 0, though neither equals the string 'true'.
  • Every temporal is in the NUMBER class too, as its epoch offset in its own natural unit: a DATE by DAYS, a TIME by seconds since midnight, a TIMESTAMP by epoch seconds, fractions included. So DATE '1970-01-02' and TIME '00:00:01' both hash as the number 1, while a DATE and the TIMESTAMP at its midnight do NOT collide — their unit differs.
  • A BINARY hashes as the STRING of its bytes: X'31' and '1' are one hash. The raw bytes are used, not a decode, so two binaries that are both invalid UTF-8 stay distinct.
  • A VARIANT hashes as the value it holds — a VARIANT number equals the bare number and a VARIANT string the bare string. The two exceptions are a VARIANT BOOLEAN and a VARIANT JSON null, which each keep their own class: neither collides with the SQL boolean nor with SQL NULL.

A container hashes as its canonical JSON, which is why key order does not matter for an object and element order does for an array, and why an array never collides with the string of its own text.

  • Method Details

    • encode

      public static byte[] encode(Object value)
      The tagged bytes for one HASH argument.