ganeshashree commented on code in PR #57559:
URL: https://github.com/apache/spark/pull/57559#discussion_r3705601488
##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/json/JsonExpressionEvalUtils.scala:
##########
@@ -350,6 +357,462 @@ case class JsonTupleEvaluator(foldableFieldNames:
Array[Option[String]]) {
}
}
+/**
+ * The three-state result of navigating a JSON path for `JSON_TABLE`.
`get_json_object` collapses
+ * "the path is absent" and "the value is JSON null" into a single `null`,
which is wrong for
+ * `JSON_TABLE`: `EXISTS` must treat a present-but-null value as existing, and
a value column must
+ * distinguish SQL `NULL` from the literal string `"null"`. This ADT keeps the
two cases distinct.
+ */
+sealed trait JsonPathResult
+object JsonPathResult {
+ /** The path did not match (the key/index is absent). */
+ case object Missing extends JsonPathResult
+ /** The path matched a JSON `null` literal. */
+ case object NullValue extends JsonPathResult
+ /** The path matched a value; `raw` is its verbatim JSON text (including
quoted strings). */
+ case class Found(raw: UTF8String) extends JsonPathResult
+}
+
+/**
+ * A prefix trie over the (wildcard-free) column paths of a single
`JSON_TABLE` invocation, built
+ * once via [[JsonTableEvaluator.buildPathTrie]] and reused for every row. It
lets
+ * [[JsonTableEvaluator.navigateAll]] resolve all columns in a single
traversal of a row item
+ * instead of re-parsing the item once per column.
+ *
+ * Each node groups the paths that share a common prefix: `named`/`indexed`
hold the object-key and
+ * array-index steps to child nodes, and `terminals` lists the result-slot
indices of the columns
+ * whose path ends exactly at this node.
+ */
+private[expressions] final class JsonTablePathTrie {
+ // Result-slot indices of columns whose path terminates at this node.
+ var terminals: List[Int] = Nil
+ // Object-key children, keyed by field name.
+ val named: mutable.HashMap[String, JsonTablePathTrie] = mutable.HashMap.empty
+ // Array-index children, keyed by index.
+ val indexed: mutable.HashMap[Long, JsonTablePathTrie] = mutable.HashMap.empty
+
+ def hasChildren: Boolean = named.nonEmpty || indexed.nonEmpty
+
+ /** True if no column path was inserted (e.g. an ordinality-only table):
nothing to resolve. */
+ def isEmpty: Boolean = terminals.isEmpty && !hasChildren
+}
+
+/**
+ * The result of positioning a parser at a JSON path for the `JSON_TABLE` row
source (see
+ * `positionAt`). Like [[JsonPathResult]] it distinguishes a missing path from
a JSON `null`, but
+ * `AtValue` leaves the parser on the matched value's first token (rather than
serializing it) so
+ * the row source can be streamed.
+ */
+sealed trait PositionResult
+object PositionResult {
+ /** The path did not match. */
+ case object Missing extends PositionResult
+ /** The path matched a JSON `null` literal. */
+ case object NullValue extends PositionResult
+ /** The path matched a value; the parser is positioned at its first token. */
+ case object AtValue extends PositionResult
+}
+
+/**
+ * Row-source and column extraction for the SQL `JSON_TABLE` function. Given
the JSON input, the
+ * (wildcard-free) container path, and whether the row path ended in `[*]`, it
produces the
+ * per-row JSON documents that the
[[org.apache.spark.sql.catalyst.expressions.JsonTable]]
+ * generator then projects into columns via [[navigate]].
Review Comment:
Fixed. Linked to `[[navigateColumns]]`, the projection entry point used by
JsonTable.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]