Class VariantUndefined

java.lang.Object
dev.frostlake.values.VariantUndefined

public final class VariantUndefined extends Object
The VARIANT undefined boundary — where a SQL NULL becomes an UndefinedNode and where the sentinel must be told apart from a JSON null.

Live-verified on a real account. With ac = ARRAY_CONSTRUCT(1,NULL,2) (which renders [1,undefined,2]) and pj = PARSE_JSON('[1,null,2]'):

   ARRAY_CONSTRUCT(1,NULL,2)  [1,NULL,2]  ARRAY_APPEND([1],NULL)  ARRAY_PREPEND([1],NULL)
   ARRAY_INSERT([1],4,9)      ARRAY_REPEAT(NULL,3)  TRANSFORM([1,2], x -> NULL)  [** NULL]
       -> every SQL NULL entering an ARRAY becomes `undefined`
   PARSE_JSON('[1,null,2]')   ARRAY_CONSTRUCT(PARSE_JSON('null'))  OBJECT_CONSTRUCT_KEEP_NULL('k',NULL)
       -> keep the JSON null; TO_ARRAY(NULL) is SQL NULL, OBJECT_CONSTRUCT('k',NULL) is {}

   TYPEOF(GET(ac,1))    -> SQL NULL      TYPEOF(GET(pj,1))    -> 'NULL_VALUE'
   GET(ac,1) IS NULL    -> TRUE          GET(pj,1) IS NULL    -> FALSE
   IS_NULL_VALUE(GET(ac,1)) -> SQL NULL  IS_NULL_VALUE(GET(pj,1)) -> TRUE
   TO_JSON(ac)          -> [1,undefined,2]                    ac = pj -> FALSE
 

Because an undefined never escapes the variant layer as a value, the sentinel deliberately reports JsonNode.isNull() — every consumer that has not heard of it reads SQL NULL, which is the right answer. Only the sites that must distinguish the two nulls call isUndefined(JsonNode).

An undefined lives only in an ARRAY. As an OBJECT member or as a whole VARIANT it degrades: live, PARSE_JSON('{"a":undefined}') is {"a":null} and PARSE_JSON('undefined') is SQL NULL — both handled by restore(JsonNode).

  • Method Summary

    Modifier and Type
    Method
    Description
    static tools.jackson.databind.JsonNode
    asObjectMember(tools.jackson.databind.JsonNode node)
    The node an ARRAY element takes when it MOVES INTO an object: an undefined degrades to a JSON null, everything else passes through.
    static Object
    elementValue(tools.jackson.databind.JsonNode node)
    The runtime value an ARRAY element binds to in a lambda or any other scalar context: an undefined is SQL NULL, everything else is its typed semi-structured value.
    static boolean
    isUndefined(tools.jackson.databind.JsonNode node)
    Whether this tree node is the undefined sentinel rather than a JSON null (or anything else).
    static String
    Replaces every bare undefined token OUTSIDE of double-quoted strings with MARKER_LITERAL so a JSON parser accepts it.
    static boolean
    A cheap pre-check: the text cannot hold a bare undefined token unless it contains the word.
    static tools.jackson.databind.JsonNode
    The undefined sentinel node.
    static tools.jackson.databind.JsonNode
    readTree(tools.jackson.databind.ObjectMapper mapper, String text)
    Parses JSON text that may carry Snowflake's bare undefined tokens.
    static tools.jackson.databind.JsonNode
    restore(tools.jackson.databind.JsonNode node)
    Turns every placeholder left by markTokens(String) into the sentinel, applying Snowflake's placement rules: an ARRAY element keeps undefined, an OBJECT member degrades to a JSON null (live: PARSE_JSON('{"a":undefined}') is {"a":null}) and a whole-value undefined is returned as the sentinel for the caller to read as SQL NULL (live: PARSE_JSON('undefined') is SQL NULL).

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Method Details

    • node

      public static tools.jackson.databind.JsonNode node()
      The undefined sentinel node.
    • isUndefined

      public static boolean isUndefined(tools.jackson.databind.JsonNode node)
      Whether this tree node is the undefined sentinel rather than a JSON null (or anything else).
    • asObjectMember

      public static tools.jackson.databind.JsonNode asObjectMember(tools.jackson.databind.JsonNode node)
      The node an ARRAY element takes when it MOVES INTO an object: an undefined degrades to a JSON null, everything else passes through. Live-verified: ARRAYS_ZIP(ARRAY_CONSTRUCT(1,NULL), ARRAY_CONSTRUCT(2,3)) is [{"$1":1,"$2":2},{"$1":null,"$2":3}] and ARRAYS_TO_OBJECT(['a','b'], ARRAY_CONSTRUCT(1,NULL)) is {"a":1,"b":null}.
    • elementValue

      public static Object elementValue(tools.jackson.databind.JsonNode node)
      The runtime value an ARRAY element binds to in a lambda or any other scalar context: an undefined is SQL NULL, everything else is its typed semi-structured value. Live-verified REDUCE(ARRAY_CONSTRUCT(1,NULL,2), 0, (acc,x) -> acc + COALESCE(x::int,10)) is 13, so the lambda saw SQL NULL for the middle element.
    • readTree

      public static tools.jackson.databind.JsonNode readTree(tools.jackson.databind.ObjectMapper mapper, String text)
      Parses JSON text that may carry Snowflake's bare undefined tokens. Text without one is handed straight to mapper, so the ordinary path is untouched.
    • mayContainToken

      public static boolean mayContainToken(String text)
      A cheap pre-check: the text cannot hold a bare undefined token unless it contains the word.
    • markTokens

      public static String markTokens(String text)
      Replaces every bare undefined token OUTSIDE of double-quoted strings with MARKER_LITERAL so a JSON parser accepts it. Word boundaries are honored (myundefined stays untouched) and text inside string values is never rewritten, so ["undefined"] keeps its genuine string element.
    • restore

      public static tools.jackson.databind.JsonNode restore(tools.jackson.databind.JsonNode node)
      Turns every placeholder left by markTokens(String) into the sentinel, applying Snowflake's placement rules: an ARRAY element keeps undefined, an OBJECT member degrades to a JSON null (live: PARSE_JSON('{"a":undefined}') is {"a":null}) and a whole-value undefined is returned as the sentinel for the caller to read as SQL NULL (live: PARSE_JSON('undefined') is SQL NULL).