Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "datafusion-functions-json"
version = "0.52.0"
version = "0.53.0"
edition = "2021"
description = "JSON functions for DataFusion"
readme = "README.md"
Expand All @@ -11,14 +11,16 @@ repository = "https://github.com/datafusion-contrib/datafusion-functions-json/"
rust-version = "1.88.0"

[dependencies]
datafusion = { version = "52", default-features = false, features = ["sql"] }
jiter = "0.12.0"
datafusion = { version = "53.0.0", default-features = false, features = [
"sql",
] }
jiter = "0.13.0"
log = "0.4"
paste = "1"

[dev-dependencies]
codspeed-criterion-compat = "2.6"
datafusion = { version = "52", default-features = false, features = [
datafusion = { version = "53.0.0", default-features = false, features = [
"nested_expressions",
"sql",
] }
Expand All @@ -35,3 +37,13 @@ pedantic = { level = "deny", priority = -1 }
[[bench]]
name = "main"
harness = false

# [patch.crates-io]
# datafusion = { git = "https://github.com/apache/datafusion.git", rev = "d47bd599ee911eb4be018595b860341ff437ccb1" }
# datafusion-common = { git = "https://github.com/apache/datafusion.git", rev = "d47bd599ee911eb4be018595b860341ff437ccb1" }
# datafusion-expr = { git = "https://github.com/apache/datafusion.git", rev = "d47bd599ee911eb4be018595b860341ff437ccb1" }
# datafusion-execution = { git = "https://github.com/apache/datafusion.git", rev = "d47bd599ee911eb4be018595b860341ff437ccb1" }
# datafusion-sql = { git = "https://github.com/apache/datafusion.git", rev = "d47bd599ee911eb4be018595b860341ff437ccb1" }
# datafusion-proto = { git = "https://github.com/apache/datafusion.git", rev = "d47bd599ee911eb4be018595b860341ff437ccb1" }
# datafusion-datasource = { git = "https://github.com/apache/datafusion.git", rev = "d47bd599ee911eb4be018595b860341ff437ccb1" }
# datafusion-physical-expr-adapter = { git = "https://github.com/apache/datafusion.git", rev = "d47bd599ee911eb4be018595b860341ff437ccb1" }
18 changes: 18 additions & 0 deletions src/json_as_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,24 @@ impl ScalarUDFImpl for JsonAsText {
fn aliases(&self) -> &[String] {
&self.aliases
}

fn placement(
&self,
args: &[datafusion::logical_expr::ExpressionPlacement],
) -> datafusion::logical_expr::ExpressionPlacement {
// If the first argument is a column and the remaining arguments are literals (a path)
// then we can push this UDF down to the leaf nodes.
if args.len() >= 2
&& matches!(args[0], datafusion::logical_expr::ExpressionPlacement::Column)
&& args[1..]
.iter()
.all(|arg| matches!(arg, datafusion::logical_expr::ExpressionPlacement::Literal))
{
datafusion::logical_expr::ExpressionPlacement::MoveTowardsLeafNodes
} else {
datafusion::logical_expr::ExpressionPlacement::KeepInPlace
}
}
}

impl InvokeResult for StringArray {
Expand Down
18 changes: 18 additions & 0 deletions src/json_contains.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,24 @@ impl ScalarUDFImpl for JsonContains {
fn aliases(&self) -> &[String] {
&self.aliases
}

fn placement(
&self,
args: &[datafusion::logical_expr::ExpressionPlacement],
) -> datafusion::logical_expr::ExpressionPlacement {
// If the first argument is a column and the remaining arguments are literals (a path)
// then we can push this UDF down to the leaf nodes.
if args.len() >= 2
&& matches!(args[0], datafusion::logical_expr::ExpressionPlacement::Column)
&& args[1..]
.iter()
.all(|arg| matches!(arg, datafusion::logical_expr::ExpressionPlacement::Literal))
{
datafusion::logical_expr::ExpressionPlacement::MoveTowardsLeafNodes
} else {
datafusion::logical_expr::ExpressionPlacement::KeepInPlace
}
}
}

impl InvokeResult for BooleanArray {
Expand Down
18 changes: 18 additions & 0 deletions src/json_get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,24 @@ impl ScalarUDFImpl for JsonGet {
fn aliases(&self) -> &[String] {
&self.aliases
}

fn placement(
&self,
args: &[datafusion::logical_expr::ExpressionPlacement],
) -> datafusion::logical_expr::ExpressionPlacement {
// If the first argument is a column and the remaining arguments are literals (a path)
// then we can push this UDF down to the leaf nodes.
if args.len() >= 2
&& matches!(args[0], datafusion::logical_expr::ExpressionPlacement::Column)
&& args[1..]
.iter()
.all(|arg| matches!(arg, datafusion::logical_expr::ExpressionPlacement::Literal))
{
datafusion::logical_expr::ExpressionPlacement::MoveTowardsLeafNodes
} else {
datafusion::logical_expr::ExpressionPlacement::KeepInPlace
}
}
}

impl InvokeResult for JsonUnion {
Expand Down
18 changes: 18 additions & 0 deletions src/json_get_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,24 @@ impl ScalarUDFImpl for JsonGetArray {
fn aliases(&self) -> &[String] {
&self.aliases
}

fn placement(
&self,
args: &[datafusion::logical_expr::ExpressionPlacement],
) -> datafusion::logical_expr::ExpressionPlacement {
// If the first argument is a column and the remaining arguments are literals (a path)
// then we can push this UDF down to the leaf nodes.
if args.len() >= 2
&& matches!(args[0], datafusion::logical_expr::ExpressionPlacement::Column)
&& args[1..]
.iter()
.all(|arg| matches!(arg, datafusion::logical_expr::ExpressionPlacement::Literal))
{
datafusion::logical_expr::ExpressionPlacement::MoveTowardsLeafNodes
} else {
datafusion::logical_expr::ExpressionPlacement::KeepInPlace
}
}
}

#[derive(Debug)]
Expand Down
18 changes: 18 additions & 0 deletions src/json_get_bool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,24 @@ impl ScalarUDFImpl for JsonGetBool {
fn aliases(&self) -> &[String] {
&self.aliases
}

fn placement(
&self,
args: &[datafusion::logical_expr::ExpressionPlacement],
) -> datafusion::logical_expr::ExpressionPlacement {
// If the first argument is a column and the remaining arguments are literals (a path)
// then we can push this UDF down to the leaf nodes.
if args.len() >= 2
&& matches!(args[0], datafusion::logical_expr::ExpressionPlacement::Column)
&& args[1..]
.iter()
.all(|arg| matches!(arg, datafusion::logical_expr::ExpressionPlacement::Literal))
{
datafusion::logical_expr::ExpressionPlacement::MoveTowardsLeafNodes
} else {
datafusion::logical_expr::ExpressionPlacement::KeepInPlace
}
}
}

fn jiter_json_get_bool(json_data: Option<&str>, path: &[JsonPath]) -> Result<bool, GetError> {
Expand Down
18 changes: 18 additions & 0 deletions src/json_get_float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,24 @@ impl ScalarUDFImpl for JsonGetFloat {
fn aliases(&self) -> &[String] {
&self.aliases
}

fn placement(
&self,
args: &[datafusion::logical_expr::ExpressionPlacement],
) -> datafusion::logical_expr::ExpressionPlacement {
// If the first argument is a column and the remaining arguments are literals (a path)
// then we can push this UDF down to the leaf nodes.
if args.len() >= 2
&& matches!(args[0], datafusion::logical_expr::ExpressionPlacement::Column)
&& args[1..]
.iter()
.all(|arg| matches!(arg, datafusion::logical_expr::ExpressionPlacement::Literal))
{
datafusion::logical_expr::ExpressionPlacement::MoveTowardsLeafNodes
} else {
datafusion::logical_expr::ExpressionPlacement::KeepInPlace
}
}
}

impl InvokeResult for Float64Array {
Expand Down
18 changes: 18 additions & 0 deletions src/json_get_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,24 @@ impl ScalarUDFImpl for JsonGetInt {
fn aliases(&self) -> &[String] {
&self.aliases
}

fn placement(
&self,
args: &[datafusion::logical_expr::ExpressionPlacement],
) -> datafusion::logical_expr::ExpressionPlacement {
// If the first argument is a column and the remaining arguments are literals (a path)
// then we can push this UDF down to the leaf nodes.
if args.len() >= 2
&& matches!(args[0], datafusion::logical_expr::ExpressionPlacement::Column)
&& args[1..]
.iter()
.all(|arg| matches!(arg, datafusion::logical_expr::ExpressionPlacement::Literal))
{
datafusion::logical_expr::ExpressionPlacement::MoveTowardsLeafNodes
} else {
datafusion::logical_expr::ExpressionPlacement::KeepInPlace
}
}
}

impl InvokeResult for Int64Array {
Expand Down
18 changes: 18 additions & 0 deletions src/json_get_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,24 @@ impl ScalarUDFImpl for JsonGetJson {
fn aliases(&self) -> &[String] {
&self.aliases
}

fn placement(
&self,
args: &[datafusion::logical_expr::ExpressionPlacement],
) -> datafusion::logical_expr::ExpressionPlacement {
// If the first argument is a column and the remaining arguments are literals (a path)
// then we can push this UDF down to the leaf nodes.
if args.len() >= 2
&& matches!(args[0], datafusion::logical_expr::ExpressionPlacement::Column)
&& args[1..]
.iter()
.all(|arg| matches!(arg, datafusion::logical_expr::ExpressionPlacement::Literal))
{
datafusion::logical_expr::ExpressionPlacement::MoveTowardsLeafNodes
} else {
datafusion::logical_expr::ExpressionPlacement::KeepInPlace
}
}
}

fn jiter_json_get_json(opt_json: Option<&str>, path: &[JsonPath]) -> Result<String, GetError> {
Expand Down
18 changes: 18 additions & 0 deletions src/json_get_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,24 @@ impl ScalarUDFImpl for JsonGetStr {
fn aliases(&self) -> &[String] {
&self.aliases
}

fn placement(
&self,
args: &[datafusion::logical_expr::ExpressionPlacement],
) -> datafusion::logical_expr::ExpressionPlacement {
// If the first argument is a column and the remaining arguments are literals (a path)
// then we can push this UDF down to the leaf nodes.
if args.len() >= 2
&& matches!(args[0], datafusion::logical_expr::ExpressionPlacement::Column)
&& args[1..]
.iter()
.all(|arg| matches!(arg, datafusion::logical_expr::ExpressionPlacement::Literal))
{
datafusion::logical_expr::ExpressionPlacement::MoveTowardsLeafNodes
} else {
datafusion::logical_expr::ExpressionPlacement::KeepInPlace
}
}
}

fn jiter_json_get_str(json_data: Option<&str>, path: &[JsonPath]) -> Result<String, GetError> {
Expand Down
18 changes: 18 additions & 0 deletions src/json_length.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,24 @@ impl ScalarUDFImpl for JsonLength {
fn aliases(&self) -> &[String] {
&self.aliases
}

fn placement(
&self,
args: &[datafusion::logical_expr::ExpressionPlacement],
) -> datafusion::logical_expr::ExpressionPlacement {
// If the first argument is a column and the remaining arguments are literals (a path)
// then we can push this UDF down to the leaf nodes.
if args.len() >= 2
&& matches!(args[0], datafusion::logical_expr::ExpressionPlacement::Column)
&& args[1..]
.iter()
.all(|arg| matches!(arg, datafusion::logical_expr::ExpressionPlacement::Literal))
{
datafusion::logical_expr::ExpressionPlacement::MoveTowardsLeafNodes
} else {
datafusion::logical_expr::ExpressionPlacement::KeepInPlace
}
}
}

impl InvokeResult for UInt64Array {
Expand Down
18 changes: 18 additions & 0 deletions src/json_object_keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,24 @@ impl ScalarUDFImpl for JsonObjectKeys {
fn aliases(&self) -> &[String] {
&self.aliases
}

fn placement(
&self,
args: &[datafusion::logical_expr::ExpressionPlacement],
) -> datafusion::logical_expr::ExpressionPlacement {
// If the first argument is a column and the remaining arguments are literals (a path)
// then we can push this UDF down to the leaf nodes.
if args.len() >= 2
&& matches!(args[0], datafusion::logical_expr::ExpressionPlacement::Column)
&& args[1..]
.iter()
.all(|arg| matches!(arg, datafusion::logical_expr::ExpressionPlacement::Literal))
{
datafusion::logical_expr::ExpressionPlacement::MoveTowardsLeafNodes
} else {
datafusion::logical_expr::ExpressionPlacement::KeepInPlace
}
}
}

/// Struct used to build a `ListArray` from the result of `jiter_json_object_keys`.
Expand Down
6 changes: 3 additions & 3 deletions tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -762,9 +762,9 @@ async fn test_plan_json_get_cte() {
select name, json_get(j, 0) v from t
";
let expected = [
"Projection: t.name, json_get(t.j, Int64(0)) AS v",
"Projection: t.name, __datafusion_extracted_1 AS v",
" SubqueryAlias: t",
" Projection: test.name, json_get(test.json_data, Utf8(\"foo\")) AS j",
" Projection: test.name, json_get(json_get(test.json_data, Utf8(\"foo\")), Int64(0)) AS __datafusion_extracted_1",
" TableScan: test projection=[name, json_data]",
];

Expand Down Expand Up @@ -1255,7 +1255,7 @@ async fn test_plan_double_arrow_double_nested_cast() {

// NB: json_as_text(..)::int is NOT the same as `json_get_int(..)`, hence the cast is not rewritten
let expected = [
"Projection: CAST(json_as_text(test.json_data, Utf8(\"foo\"), Int64(0)) AS json_data ->> 'foo' ->> 0 AS Int32)",
"Projection: CAST(json_as_text(test.json_data, Utf8(\"foo\"), Int64(0)) AS Int32) AS json_data ->> 'foo' ->> 0",
" TableScan: test projection=[json_data]",
];

Expand Down
Loading