feat(json2): json_get respect type hints [Backport release/v1.3] (#9511)

feat(json2): json_get respect type hints (#9349)


(cherry picked from commit 9a57e1195d)

Signed-off-by: fys <fengys1996@gmail.com>
Co-authored-by: fys <40801205+fengys1996@users.noreply.github.com>
This commit is contained in:
LFC
2026-10-12 10:02:02 +08:00
committed by GitHub
co-authored by fys
parent 34283e9b85
commit 1a25a02f21
5 changed files with 620 additions and 129 deletions
Generated
+35 -35
View File
@@ -3729,7 +3729,7 @@ checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
[[package]]
name = "datafusion"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"arrow-schema 59.2.0",
@@ -3781,7 +3781,7 @@ dependencies = [
[[package]]
name = "datafusion-catalog"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"async-trait",
@@ -3805,7 +3805,7 @@ dependencies = [
[[package]]
name = "datafusion-catalog-listing"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"async-trait",
@@ -3828,7 +3828,7 @@ dependencies = [
[[package]]
name = "datafusion-common"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"arrow-ipc 59.2.0",
@@ -3854,7 +3854,7 @@ dependencies = [
[[package]]
name = "datafusion-common-runtime"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"futures",
"log",
@@ -3864,7 +3864,7 @@ dependencies = [
[[package]]
name = "datafusion-datasource"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"async-compression",
@@ -3900,7 +3900,7 @@ dependencies = [
[[package]]
name = "datafusion-datasource-arrow"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"arrow-ipc 59.2.0",
@@ -3924,7 +3924,7 @@ dependencies = [
[[package]]
name = "datafusion-datasource-csv"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"async-trait",
@@ -3947,7 +3947,7 @@ dependencies = [
[[package]]
name = "datafusion-datasource-json"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"async-trait",
@@ -3970,7 +3970,7 @@ dependencies = [
[[package]]
name = "datafusion-datasource-parquet"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"arrow-schema 59.2.0",
@@ -4002,12 +4002,12 @@ dependencies = [
[[package]]
name = "datafusion-doc"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
[[package]]
name = "datafusion-execution"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"arrow-buffer 59.2.0",
@@ -4032,7 +4032,7 @@ dependencies = [
[[package]]
name = "datafusion-expr"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"arrow-schema 59.2.0",
@@ -4056,7 +4056,7 @@ dependencies = [
[[package]]
name = "datafusion-expr-common"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"datafusion-common",
@@ -4067,7 +4067,7 @@ dependencies = [
[[package]]
name = "datafusion-functions"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"arrow-buffer 59.2.0",
@@ -4098,7 +4098,7 @@ dependencies = [
[[package]]
name = "datafusion-functions-aggregate"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"datafusion-common",
@@ -4118,7 +4118,7 @@ dependencies = [
[[package]]
name = "datafusion-functions-aggregate-common"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"datafusion-common",
@@ -4129,7 +4129,7 @@ dependencies = [
[[package]]
name = "datafusion-functions-nested"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"arrow-ord 59.2.0",
@@ -4153,7 +4153,7 @@ dependencies = [
[[package]]
name = "datafusion-functions-table"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"async-trait",
@@ -4168,7 +4168,7 @@ dependencies = [
[[package]]
name = "datafusion-functions-window"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"datafusion-common",
@@ -4184,7 +4184,7 @@ dependencies = [
[[package]]
name = "datafusion-functions-window-common"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"datafusion-common",
"datafusion-physical-expr-common",
@@ -4193,7 +4193,7 @@ dependencies = [
[[package]]
name = "datafusion-macros"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"datafusion-doc",
"quote",
@@ -4203,7 +4203,7 @@ dependencies = [
[[package]]
name = "datafusion-optimizer"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"chrono",
@@ -4251,7 +4251,7 @@ dependencies = [
[[package]]
name = "datafusion-physical-expr"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"arrow-schema 59.2.0",
@@ -4274,7 +4274,7 @@ dependencies = [
[[package]]
name = "datafusion-physical-expr-adapter"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"datafusion-common",
@@ -4288,7 +4288,7 @@ dependencies = [
[[package]]
name = "datafusion-physical-expr-common"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"chrono",
@@ -4305,7 +4305,7 @@ dependencies = [
[[package]]
name = "datafusion-physical-optimizer"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"datafusion-common",
@@ -4324,7 +4324,7 @@ dependencies = [
[[package]]
name = "datafusion-physical-plan"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"arrow-data 59.2.0",
@@ -4361,7 +4361,7 @@ dependencies = [
[[package]]
name = "datafusion-proto"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"datafusion-catalog",
@@ -4387,7 +4387,7 @@ dependencies = [
[[package]]
name = "datafusion-proto-common"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"datafusion-common",
@@ -4397,7 +4397,7 @@ dependencies = [
[[package]]
name = "datafusion-proto-models"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"datafusion-common",
"datafusion-proto-common",
@@ -4407,7 +4407,7 @@ dependencies = [
[[package]]
name = "datafusion-pruning"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"datafusion-common",
@@ -4422,7 +4422,7 @@ dependencies = [
[[package]]
name = "datafusion-session"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow-schema 59.2.0",
"async-trait",
@@ -4436,7 +4436,7 @@ dependencies = [
[[package]]
name = "datafusion-sql"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"arrow 59.2.0",
"bigdecimal 0.4.8",
@@ -4455,7 +4455,7 @@ dependencies = [
[[package]]
name = "datafusion-substrait"
version = "55.1.0"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=4bd567a884dcbfd89d0f0a7859ef8367e38fff97#4bd567a884dcbfd89d0f0a7859ef8367e38fff97"
source = "git+https://github.com/GreptimeTeam/datafusion.git?rev=e3221b8f74b15c7192621576e5d1e354a6cf097a#e3221b8f74b15c7192621576e5d1e354a6cf097a"
dependencies = [
"async-recursion",
"async-trait",
+15 -15
View File
@@ -363,21 +363,21 @@ rev = "f743c3a5fe8c1f57363eac5866c356b7d91f9b39"
[patch.crates-io]
substrait = { git = "https://github.com/GreptimeTeam/substrait-rs.git", rev = "91ec978b0649417ad3da8390e7a515baec723b1b" }
datafusion = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "4bd567a884dcbfd89d0f0a7859ef8367e38fff97" }
datafusion-common = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "4bd567a884dcbfd89d0f0a7859ef8367e38fff97" }
datafusion-datasource = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "4bd567a884dcbfd89d0f0a7859ef8367e38fff97" }
datafusion-expr = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "4bd567a884dcbfd89d0f0a7859ef8367e38fff97" }
datafusion-expr-common = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "4bd567a884dcbfd89d0f0a7859ef8367e38fff97" }
datafusion-functions = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "4bd567a884dcbfd89d0f0a7859ef8367e38fff97" }
datafusion-functions-aggregate-common = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "4bd567a884dcbfd89d0f0a7859ef8367e38fff97" }
datafusion-functions-window-common = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "4bd567a884dcbfd89d0f0a7859ef8367e38fff97" }
datafusion-optimizer = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "4bd567a884dcbfd89d0f0a7859ef8367e38fff97" }
datafusion-physical-expr = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "4bd567a884dcbfd89d0f0a7859ef8367e38fff97" }
datafusion-physical-expr-common = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "4bd567a884dcbfd89d0f0a7859ef8367e38fff97" }
datafusion-physical-plan = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "4bd567a884dcbfd89d0f0a7859ef8367e38fff97" }
datafusion-proto = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "4bd567a884dcbfd89d0f0a7859ef8367e38fff97" }
datafusion-sql = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "4bd567a884dcbfd89d0f0a7859ef8367e38fff97" }
datafusion-substrait = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "4bd567a884dcbfd89d0f0a7859ef8367e38fff97" }
datafusion = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "e3221b8f74b15c7192621576e5d1e354a6cf097a" }
datafusion-common = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "e3221b8f74b15c7192621576e5d1e354a6cf097a" }
datafusion-datasource = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "e3221b8f74b15c7192621576e5d1e354a6cf097a" }
datafusion-expr = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "e3221b8f74b15c7192621576e5d1e354a6cf097a" }
datafusion-expr-common = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "e3221b8f74b15c7192621576e5d1e354a6cf097a" }
datafusion-functions = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "e3221b8f74b15c7192621576e5d1e354a6cf097a" }
datafusion-functions-aggregate-common = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "e3221b8f74b15c7192621576e5d1e354a6cf097a" }
datafusion-functions-window-common = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "e3221b8f74b15c7192621576e5d1e354a6cf097a" }
datafusion-optimizer = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "e3221b8f74b15c7192621576e5d1e354a6cf097a" }
datafusion-physical-expr = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "e3221b8f74b15c7192621576e5d1e354a6cf097a" }
datafusion-physical-expr-common = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "e3221b8f74b15c7192621576e5d1e354a6cf097a" }
datafusion-physical-plan = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "e3221b8f74b15c7192621576e5d1e354a6cf097a" }
datafusion-proto = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "e3221b8f74b15c7192621576e5d1e354a6cf097a" }
datafusion-sql = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "e3221b8f74b15c7192621576e5d1e354a6cf097a" }
datafusion-substrait = { git = "https://github.com/GreptimeTeam/datafusion.git", rev = "e3221b8f74b15c7192621576e5d1e354a6cf097a" }
datafusion-pg-catalog = { git = "https://github.com/datafusion-contrib/datafusion-postgres.git", rev = "ffb14a52b3d7c7489559812e1ec38096f225ce93" }
arrow-pg = { git = "https://github.com/datafusion-contrib/datafusion-postgres.git", rev = "ffb14a52b3d7c7489559812e1ec38096f225ce93" }
sqlparser = { git = "https://github.com/GreptimeTeam/sqlparser-rs.git", rev = "9e9019bb1c7040ed956f654e39378dd42ab17884" }
+212 -24
View File
@@ -16,11 +16,12 @@ use std::sync::{Arc, LazyLock};
use arrow_schema::Field;
use arrow_schema::extension::ExtensionType;
use common_function::scalars::json::json_get::JsonGetWithType;
use common_function::scalars::json::json_get::{JsonGetWithType, parse_json_get_path};
use common_function::scalars::udf::create_udf;
use datafusion_common::arrow::datatypes::DataType;
use datafusion_common::{
Column, DFSchema, DataFusionError, Result, ScalarValue, TableReference, plan_datafusion_err,
Column, DFSchema, DataFusionError, ExprSchema, Result, ScalarValue, TableReference,
plan_datafusion_err,
};
use datafusion_expr::expr::{BinaryExpr, ScalarFunction};
use datafusion_expr::planner::{
@@ -35,6 +36,7 @@ use datatypes::extension::json::{
Json2ExtensionType, is_json2_extension_type, parse_legacy_json2_settings,
};
use datatypes::types::json_type::JsonNativeType;
use jsonb::jsonpath::Path;
use sqlparser::ast::BinaryOperator;
/// Rewrites JSON-aware SQL expressions into DataFusion expressions.
@@ -44,6 +46,8 @@ use sqlparser::ast::BinaryOperator;
/// For example, `select a.b.c` => `select json_get(a, '$.b.c')`.
/// - Extends a JSON path with list indexes and fields following an index.
/// For example, `select a.b[0].c` => `select json_get(a, '$.b[0].c')`.
/// - Applies JSON2 path type hints to both compound identifiers and explicit `json_get` calls
/// before inferring types from their enclosing expressions.
/// - Pushes an "expected type" argument into the `json_get` function when it participates in a
/// binary operator. So that `json_get` knows the wanted data type when dealing with variant
/// JSON values.
@@ -152,12 +156,7 @@ impl ExprPlanner for JsonExprPlanner {
Expr::Column(Column::from((qualifier, field))),
Expr::Literal(ScalarValue::Utf8(Some(path)), None),
];
if let Some(json_type) = json_type_hint(field, nested_names)? {
args.push(Expr::Literal(
ScalarValue::try_new_null(&json_type.as_arrow_type())?,
None,
));
}
apply_json_type_hint(field, nested_names, &mut args)?;
Ok(PlannerResult::Planned(Expr::ScalarFunction(
ScalarFunction::new_udf(json_get, args),
@@ -168,7 +167,27 @@ impl ExprPlanner for JsonExprPlanner {
///
/// `Original` carries the possibly modified raw expression to subsequent planners and then
/// DataFusion's default function construction. Returning `Planned` would short-circuit both.
fn plan_scalar(&self, mut expr: RawScalarExpr) -> Result<PlannerResult<RawScalarExpr>> {
fn plan_scalar(
&self,
mut expr: RawScalarExpr,
schema: &DFSchema,
) -> Result<PlannerResult<RawScalarExpr>> {
if expr.func.name().eq_ignore_ascii_case(JsonGetWithType::NAME) && expr.args.len() == 2 {
let field = match &expr.args[0] {
// Resolving an unqualified column may require JOIN USING information unavailable
// in this schema. Skip hint injection if lookup fails and let DataFusion resolve
// the column or report the error later.
Expr::Column(column) => schema.field_from_column(column).ok().cloned(),
// Outer references carry their field metadata from the outer query's schema.
Expr::OuterReferenceColumn(field, _) => Some(Arc::clone(field)),
_ => None,
};
if let Some(field) = field
&& let Some(path) = json_get_object_path(&expr.args[1])
{
apply_json_type_hint(&field, &path, &mut expr.args)?;
}
}
push_function_arg_types(expr.func.as_ref(), &mut expr.args)?;
Ok(PlannerResult::Original(expr))
}
@@ -196,6 +215,46 @@ impl ExprPlanner for JsonExprPlanner {
}
}
/// Sets the native read type for an exact hinted object path. Both SQL syntaxes use this before
/// an enclosing expression can infer a type or push down a cast.
fn apply_json_type_hint(field: &Field, path: &[String], args: &mut Vec<Expr>) -> Result<()> {
if !is_json2_extension_type(field) || args.len() != 2 {
return Ok(());
}
if let Some(json_type) = json_type_hint(field, path)? {
args.push(Expr::Literal(
ScalarValue::try_new_null(&json_type.as_arrow_type())?,
None,
));
}
Ok(())
}
/// Resolves `json_get`'s second argument expression (the path) to object field names for type hint
/// lookup. For example, the string literal `'$.user.age'` resolves to `["user", "age"]`.
///
/// Returns `None` for non-string literals, non-literal expressions, invalid JSONPaths, or paths
/// containing array indexes, wildcards, or filters.
/// Returning `None` only skips hint injection; the original `json_get` expression is preserved.
fn json_get_object_path(expr: &Expr) -> Option<Vec<String>> {
let path = expr.as_literal()?.try_as_str().flatten()?;
// Invalid paths skip hint lookup here. Later planning still validates the path and reports
// the error, preserving the existing JSONPath error behavior.
let json_path = parse_json_get_path(path).ok()?;
let mut names = Vec::new();
for segment in json_path.paths {
match segment {
Path::Root => {}
Path::DotField(name) | Path::ColonField(name) | Path::ObjectField(name) => {
names.push(name.into_owned());
}
// Array indexes, wildcards and filters do not identify a hinted object path.
_ => return None,
}
}
Some(names)
}
/// Returns the configured native type for an exact JSON2 object path.
fn json_type_hint(field: &Field, path: &[String]) -> Result<Option<JsonNativeType>> {
let settings = if field.extension_type_name() == Some(Json2ExtensionType::NAME) {
@@ -705,15 +764,138 @@ mod tests {
Ok(())
}
#[test]
fn test_plan_json_get_applies_type_hint() -> Result<()> {
let settings = JsonSettings::try_new(
vec![
JsonTypeHint {
path: vec!["payload".to_string(), "cpu".to_string()],
data_type: ConcreteDataType::int64_datatype(),
inverted_index: false,
},
JsonTypeHint {
path: vec!["payload.cpu".to_string()],
data_type: ConcreteDataType::string_datatype(),
inverted_index: false,
},
],
None,
)
.unwrap();
let field = Field::new("j", DataType::Struct(Fields::empty()), true).with_extension_type(
Json2ExtensionType::new(Arc::new(JsonMetadata::new(settings))),
);
let relation = TableReference::bare("events");
let schema = DFSchema::new_with_metadata(
vec![
(Some(relation.clone()), Arc::new(field.clone())),
(
Some(TableReference::bare("other")),
Arc::new(Field::new("j", DataType::Binary, true)),
),
],
Default::default(),
)?;
let base = Expr::Column(Column::new(Some(relation.clone()), "j"));
for (path, names, expected) in [
("payload.cpu", vec!["payload", "cpu"], Some(DataType::Int64)),
(
"$.payload.cpu",
vec!["payload", "cpu"],
Some(DataType::Int64),
),
(
r#"$."payload.cpu""#,
vec!["payload.cpu"],
Some(DataType::Utf8View),
),
("missing", vec!["missing"], None),
] {
let Expr::ScalarFunction(function) = json_get_expr(base.clone(), path) else {
unreachable!();
};
let PlannerResult::Original(scalar) = JsonExprPlanner.plan_scalar(
RawScalarExpr {
func: function.func,
args: function.args,
},
&schema,
)?
else {
unreachable!();
};
let direct = Expr::ScalarFunction(ScalarFunction::new_udf(scalar.func, scalar.args));
let names = names.into_iter().map(String::from).collect::<Vec<_>>();
let PlannerResult::Planned(dotted) =
JsonExprPlanner.plan_compound_identifier(&field, Some(&relation), &names)?
else {
unreachable!();
};
assert_eq!(expected, extract_json_get_type(&direct), "{path}");
assert_eq!(
extract_json_get_type(&dotted),
extract_json_get_type(&direct)
);
// Outer function inference must not replace a declared hint.
if expected.is_some() {
let PlannerResult::Original(outer) = JsonExprPlanner.plan_scalar(
RawScalarExpr {
func: coalesce(),
args: vec![
direct.clone(),
Expr::Literal(ScalarValue::Float64(Some(1.0)), None),
],
},
&schema,
)?
else {
unreachable!();
};
assert_eq!(direct, outer.args[0]);
}
}
// Non-object paths cannot match an object type hint. A different table's unhinted
// column must not inherit the same-named JSON2 column's hints either.
for (base, path) in [
(base.clone(), "$"),
(base.clone(), "$.payload.*"),
(base.clone(), "$.payload.cpu[0]"),
(base.clone(), "$.payload["),
(Expr::Column(Column::new(Some("other"), "j")), "payload.cpu"),
] {
let Expr::ScalarFunction(function) = json_get_expr(base, path) else {
unreachable!();
};
let PlannerResult::Original(scalar) = JsonExprPlanner.plan_scalar(
RawScalarExpr {
func: function.func,
args: function.args,
},
&schema,
)?
else {
unreachable!();
};
assert_eq!(2, scalar.args.len(), "{path}");
}
Ok(())
}
#[test]
fn test_plan_functions() -> Result<()> {
let planner = JsonExprPlanner;
let json_get = || json_get_expr(Expr::Column(Column::new_unqualified("j")), "a.b");
let PlannerResult::Original(scalar) = planner.plan_scalar(RawScalarExpr {
func: abs(),
args: vec![json_get()],
})?
let PlannerResult::Original(scalar) = planner.plan_scalar(
RawScalarExpr {
func: abs(),
args: vec![json_get()],
},
&DFSchema::empty(),
)?
else {
unreachable!();
};
@@ -722,13 +904,16 @@ mod tests {
extract_json_get_type(&scalar.args[0])
);
let PlannerResult::Original(scalar) = planner.plan_scalar(RawScalarExpr {
func: power(),
args: vec![
json_get(),
Expr::Column(Column::new_unqualified("exponent")),
],
})?
let PlannerResult::Original(scalar) = planner.plan_scalar(
RawScalarExpr {
func: power(),
args: vec![
json_get(),
Expr::Column(Column::new_unqualified("exponent")),
],
},
&DFSchema::empty(),
)?
else {
unreachable!();
};
@@ -793,10 +978,13 @@ mod tests {
let mut typed = json_get();
push_json_get_type_arg(&mut typed, &DataType::Float64)?;
let PlannerResult::Original(scalar) = planner.plan_scalar(RawScalarExpr {
func: coalesce(),
args: vec![json_get(), typed],
})?
let PlannerResult::Original(scalar) = planner.plan_scalar(
RawScalarExpr {
func: coalesce(),
args: vec![json_get(), typed],
},
&DFSchema::empty(),
)?
else {
unreachable!();
};
@@ -13,9 +13,43 @@ Affected Rows: 0
INSERT INTO json2_query_respect_type_hint VALUES
(1, '{"a":9007199254740993,"b":"three"}', 2),
(2, '{"a":7,"b":"7"}', 2);
(2, '{"a":7,"b":"7"}', 2),
(3, '{}', 2),
(4, '{"a":null,"b":null}', 2);
Affected Rows: 2
Affected Rows: 4
-- Both syntaxes use the hint when reading the memtable, including missing/null paths.
SELECT j.a AS dotted, json_get(j, 'a') AS direct,
arrow_typeof(j.a) AS dotted_type, arrow_typeof(json_get(j, 'a')) AS direct_type
FROM json2_query_respect_type_hint
ORDER BY ts;
+------------------+------------------+-------------+-------------+
| dotted | direct | dotted_type | direct_type |
+------------------+------------------+-------------+-------------+
| 9007199254740993 | 9007199254740993 | Int64 | Int64 |
| 7 | 7 | Int64 | Int64 |
| | | Int64 | Int64 |
| | | Int64 | Int64 |
+------------------+------------------+-------------+-------------+
-- An explicit third argument takes precedence over the BIGINT path hint in memtable reads.
SELECT json_get(j, 'a') AS hinted_bigint,
json_get(j, 'a', NULL::DOUBLE) AS explicit_double,
arrow_typeof(json_get(j, 'a')) AS hinted_type,
arrow_typeof(json_get(j, 'a', NULL::DOUBLE)) AS explicit_type
FROM json2_query_respect_type_hint
ORDER BY ts;
+------------------+--------------------+-------------+---------------+
| hinted_bigint | explicit_double | hinted_type | explicit_type |
+------------------+--------------------+-------------+---------------+
| 9007199254740993 | 9007199254740992.0 | Int64 | Float64 |
| 7 | 7.0 | Int64 | Float64 |
| | | Int64 | Float64 |
| | | Int64 | Float64 |
+------------------+--------------------+-------------+---------------+
ADMIN FLUSH_TABLE('json2_query_respect_type_hint');
@@ -25,83 +59,172 @@ ADMIN FLUSH_TABLE('json2_query_respect_type_hint');
| 0 |
+----------------------------------------------------+
-- The explicit read type also overrides the hint in the plan and SST reads.
-- SQLNESS REPLACE (peers.*) REDACTED
EXPLAIN SELECT j.a AS hinted_bigint
EXPLAIN SELECT json_get(j, 'a', NULL::DOUBLE) AS explicit_double
FROM json2_query_respect_type_hint;
+---------------+--------------------------------------------------------------------------------------------------+
| plan_type | plan |
+---------------+--------------------------------------------------------------------------------------------------+
| logical_plan | MergeScan [is_placeholder=false, remote_input=[ |
| | Projection: json_get(json2_query_respect_type_hint.j, Utf8("$.a"), Int64(NULL)) AS hinted_bigint |
| | TableScan: json2_query_respect_type_hint |
| | ]] |
| physical_plan | CooperativeExec |
+---------------+----------------------------------------------------------------------------------------------------+
| plan_type | plan |
+---------------+----------------------------------------------------------------------------------------------------+
| logical_plan | MergeScan [is_placeholder=false, remote_input=[ |
| | Projection: json_get(json2_query_respect_type_hint.j, Utf8("a"), Float64(NULL)) AS explicit_double |
| | TableScan: json2_query_respect_type_hint |
| | ]] |
| physical_plan | CooperativeExec |
| | MergeScanExec: REDACTED
| | |
+---------------+--------------------------------------------------------------------------------------------------+
| | |
+---------------+----------------------------------------------------------------------------------------------------+
SELECT json_get(j, 'a') AS hinted_bigint,
json_get(j, 'a', NULL::DOUBLE) AS explicit_double,
arrow_typeof(json_get(j, 'a')) AS hinted_type,
arrow_typeof(json_get(j, 'a', NULL::DOUBLE)) AS explicit_type
FROM json2_query_respect_type_hint
ORDER BY ts;
+------------------+--------------------+-------------+---------------+
| hinted_bigint | explicit_double | hinted_type | explicit_type |
+------------------+--------------------+-------------+---------------+
| 9007199254740993 | 9007199254740992.0 | Int64 | Float64 |
| 7 | 7.0 | Int64 | Float64 |
| | | Int64 | Float64 |
| | | Int64 | Float64 |
+------------------+--------------------+-------------+---------------+
-- SQLNESS REPLACE (peers.*) REDACTED
EXPLAIN SELECT j.a AS hinted_bigint, json_get(j, 'a') AS direct_bigint
FROM json2_query_respect_type_hint;
+---------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| plan_type | plan |
+---------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| logical_plan | MergeScan [is_placeholder=false, remote_input=[ |
| | Projection: json_get(json2_query_respect_type_hint.j, Utf8("$.a"), Int64(NULL)) AS hinted_bigint, json_get(json2_query_respect_type_hint.j, Utf8("a"), Int64(NULL)) AS direct_bigint |
| | TableScan: json2_query_respect_type_hint |
| | ]] |
| physical_plan | CooperativeExec |
| | MergeScanExec: REDACTED
| | |
+---------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-- Uncast paths use their JSON2 type hints.
SELECT
j.a AS hinted_bigint,
j.b AS hinted_string
json_get(j, 'a') AS direct_bigint,
json_get(j, '$.a') AS rooted_bigint,
j.b AS hinted_string,
json_get(j, 'b') AS direct_string
FROM json2_query_respect_type_hint
ORDER BY ts;
+------------------+---------------+
| hinted_bigint | hinted_string |
+------------------+---------------+
| 9007199254740993 | three |
| 7 | 7 |
+------------------+---------------+
+------------------+------------------+------------------+---------------+---------------+
| hinted_bigint | direct_bigint | rooted_bigint | hinted_string | direct_string |
+------------------+------------------+------------------+---------------+---------------+
| 9007199254740993 | 9007199254740993 | 9007199254740993 | three | three |
| 7 | 7 | 7 | 7 | 7 |
| | | | | |
| | | | | |
+------------------+------------------+------------------+---------------+---------------+
-- The SST read keeps the same result types as the memtable read.
SELECT arrow_typeof(j.a) AS dotted_type, arrow_typeof(json_get(j, 'a')) AS direct_type,
arrow_typeof(j.b) AS dotted_string_type, arrow_typeof(json_get(j, 'b')) AS direct_string_type
FROM json2_query_respect_type_hint LIMIT 1;
+-------------+-------------+--------------------+--------------------+
| dotted_type | direct_type | dotted_string_type | direct_string_type |
+-------------+-------------+--------------------+--------------------+
| Int64 | Int64 | Utf8View | Utf8View |
+-------------+-------------+--------------------+--------------------+
-- A JSON2 type hint takes precedence over an explicit SQL cast for the native
-- read type.
-- SQLNESS REPLACE (peers.*) REDACTED
EXPLAIN SELECT j.a::STRING
EXPLAIN SELECT j.a::STRING, json_get(j, 'a')::STRING
FROM json2_query_respect_type_hint;
+---------------+-----------------------------------------------------------------------------------------------+
| plan_type | plan |
+---------------+-----------------------------------------------------------------------------------------------+
| logical_plan | MergeScan [is_placeholder=false, remote_input=[ |
| | Projection: CAST(json_get(json2_query_respect_type_hint.j, Utf8("$.a"), Int64(NULL)) AS Utf8) |
| | TableScan: json2_query_respect_type_hint |
| | ]] |
| physical_plan | CooperativeExec |
+---------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| plan_type | plan |
+---------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| logical_plan | MergeScan [is_placeholder=false, remote_input=[ |
| | Projection: CAST(json_get(json2_query_respect_type_hint.j, Utf8("$.a"), Int64(NULL)) AS Utf8), CAST(json_get(json2_query_respect_type_hint.j, Utf8("a"), Int64(NULL)) AS Utf8) |
| | TableScan: json2_query_respect_type_hint |
| | ]] |
| physical_plan | CooperativeExec |
| | MergeScanExec: REDACTED
| | |
+---------------+-----------------------------------------------------------------------------------------------+
| | |
+---------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-- An explicit CAST changes the expression result type.
SELECT
j.a::STRING AS cast_string
j.a::STRING AS cast_string,
json_get(j, 'a')::STRING AS direct_cast_string,
arrow_typeof(json_get(j, 'a')::STRING) AS cast_type
FROM json2_query_respect_type_hint
ORDER BY ts;
+------------------+
| cast_string |
+------------------+
| 9007199254740993 |
| 7 |
+------------------+
+------------------+--------------------+-----------+
| cast_string | direct_cast_string | cast_type |
+------------------+--------------------+-----------+
| 9007199254740993 | 9007199254740993 | Utf8 |
| 7 | 7 | Utf8 |
| | | Utf8 |
| | | Utf8 |
+------------------+--------------------+-----------+
-- A JSON2 type hint takes precedence over expression-context inference.
-- SQLNESS REPLACE (peers.*) REDACTED
EXPLAIN SELECT coalesce(j.b, exponent)
EXPLAIN SELECT coalesce(j.b, exponent), coalesce(json_get(j, 'b'), exponent)
FROM json2_query_respect_type_hint;
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| plan_type | plan |
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| logical_plan | MergeScan [is_placeholder=false, remote_input=[ |
| | Projection: CASE WHEN CAST(json_get(json2_query_respect_type_hint.j, Utf8("$.b"), Utf8View(NULL)) AS Float64) IS NOT NULL THEN CAST(json_get(json2_query_respect_type_hint.j, Utf8("$.b"), Utf8View(NULL)) AS Float64) ELSE json2_query_respect_type_hint.exponent END AS coalesce(json_get(json2_query_respect_type_hint.j,Utf8("$.b"),Utf8View(NULL)),json2_query_respect_type_hint.exponent) |
| | TableScan: json2_query_respect_type_hint |
| | ]] |
| physical_plan | CooperativeExec |
+---------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| plan_type | plan |
+---------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| logical_plan | MergeScan [is_placeholder=false, remote_input=[ |
| | Projection: CASE WHEN CAST(json_get(json2_query_respect_type_hint.j, Utf8("$.b"), Utf8View(NULL)) AS Float64) IS NOT NULL THEN CAST(json_get(json2_query_respect_type_hint.j, Utf8("$.b"), Utf8View(NULL)) AS Float64) ELSE json2_query_respect_type_hint.exponent END AS coalesce(json_get(json2_query_respect_type_hint.j,Utf8("$.b"),Utf8View(NULL)),json2_query_respect_type_hint.exponent), CASE WHEN CAST(json_get(json2_query_respect_type_hint.j, Utf8("b"), Utf8View(NULL)) AS Float64) IS NOT NULL THEN CAST(json_get(json2_query_respect_type_hint.j, Utf8("b"), Utf8View(NULL)) AS Float64) ELSE json2_query_respect_type_hint.exponent END AS coalesce(json_get(json2_query_respect_type_hint.j,Utf8("b"),Utf8View(NULL)),json2_query_respect_type_hint.exponent) |
| | TableScan: json2_query_respect_type_hint |
| | ]] |
| physical_plan | CooperativeExec |
| | MergeScanExec: REDACTED
| | |
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| | |
+---------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-- Hint injection precedes scalar, aggregate, window and binary-expression inference.
SELECT abs(j.a) AS dotted_abs, abs(json_get(j, 'a')) AS direct_abs,
j.a + 1 AS dotted_plus, json_get(j, 'a') + 1 AS direct_plus
FROM json2_query_respect_type_hint ORDER BY ts;
+------------------+------------------+------------------+------------------+
| dotted_abs | direct_abs | dotted_plus | direct_plus |
+------------------+------------------+------------------+------------------+
| 9007199254740993 | 9007199254740993 | 9007199254740994 | 9007199254740994 |
| 7 | 7 | 8 | 8 |
| | | | |
| | | | |
+------------------+------------------+------------------+------------------+
SELECT sum(j.a) AS dotted_sum, sum(json_get(j, 'a')) AS direct_sum
FROM json2_query_respect_type_hint;
+------------------+------------------+
| dotted_sum | direct_sum |
+------------------+------------------+
| 9007199254741000 | 9007199254741000 |
+------------------+------------------+
SELECT sum(j.a) OVER (ORDER BY ts) AS dotted_sum,
sum(json_get(j, 'a')) OVER (ORDER BY ts) AS direct_sum
FROM json2_query_respect_type_hint ORDER BY ts;
+------------------+------------------+
| dotted_sum | direct_sum |
+------------------+------------------+
| 9007199254740993 | 9007199254740993 |
| 9007199254741000 | 9007199254741000 |
| 9007199254741000 | 9007199254741000 |
| 9007199254741000 | 9007199254741000 |
+------------------+------------------+
-- A STRING type hint is respected, so numeric arithmetic requires an explicit cast.
SELECT j.b + exponent
@@ -109,6 +232,11 @@ FROM json2_query_respect_type_hint;
Error: 3000(PlanQuery), Failed to plan SQL: Error during planning: Cannot coerce arithmetic expression Utf8View + Float64 to valid types
SELECT json_get(j, 'b') + exponent
FROM json2_query_respect_type_hint;
Error: 3000(PlanQuery), Failed to plan SQL: Error during planning: Cannot coerce arithmetic expression Utf8View + Float64 to valid types
-- An explicit cast changes the expression type while preserving the STRING native read type.
-- The invalid string value still reports a normal SQL cast error.
SELECT j.b::DOUBLE + exponent
@@ -116,7 +244,88 @@ FROM json2_query_respect_type_hint;
Error: 3001(EngineExecuteQuery), Cast error: Cannot cast string 'three' to value of Float64 type
SELECT json_get(j, 'b')::DOUBLE + exponent
FROM json2_query_respect_type_hint;
Error: 3001(EngineExecuteQuery), Cast error: Cannot cast string 'three' to value of Float64 type
-- Resolve the input field through table aliases and a renamed derived-table column.
SELECT t.j.a AS dotted, json_get(t.j, 'a') AS direct
FROM json2_query_respect_type_hint AS t ORDER BY t.ts;
+------------------+------------------+
| dotted | direct |
+------------------+------------------+
| 9007199254740993 | 9007199254740993 |
| 7 | 7 |
| | |
| | |
+------------------+------------------+
SELECT q.payload.a AS dotted, json_get(q.payload, 'a') AS direct
FROM (SELECT ts, j AS payload FROM json2_query_respect_type_hint) AS q ORDER BY q.ts;
+------------------+------------------+
| dotted | direct |
+------------------+------------------+
| 9007199254740993 | 9007199254740993 |
| 7 | 7 |
| | |
| | |
+------------------+------------------+
DROP TABLE json2_query_respect_type_hint;
Affected Rows: 0
CREATE TABLE json2_hint_paths (
ts TIMESTAMP TIME INDEX,
j JSON2 (nested.a BIGINT, "nested.a" BOOLEAN)
) WITH (
'append_mode' = 'true'
);
Affected Rows: 0
INSERT INTO json2_hint_paths VALUES
(1, '{"nested":{"a":42},"nested.a":true,"unhinted":7}');
Affected Rows: 1
-- Quoted keys are literal names, not nested paths; unhinted paths retain their default type.
SELECT j.nested.a AS dotted_nested, json_get(j, '$.nested.a') AS direct_nested,
j."nested.a" AS dotted_key, json_get(j, '$."nested.a"') AS direct_key,
arrow_typeof(json_get(j, 'nested.a')) AS nested_type,
arrow_typeof(json_get(j, '$."nested.a"')) AS key_type,
arrow_typeof(j.unhinted) AS dotted_unhinted_type,
arrow_typeof(json_get(j, 'unhinted')) AS direct_unhinted_type
FROM json2_hint_paths;
+---------------+---------------+------------+------------+-------------+----------+----------------------+----------------------+
| dotted_nested | direct_nested | dotted_key | direct_key | nested_type | key_type | dotted_unhinted_type | direct_unhinted_type |
+---------------+---------------+------------+------------+-------------+----------+----------------------+----------------------+
| 42 | 42 | true | true | Int64 | Boolean | Utf8View | Utf8View |
+---------------+---------------+------------+------------+-------------+----------+----------------------+----------------------+
DROP TABLE json2_hint_paths;
Affected Rows: 0
-- Hint lookup must defer unqualified JOIN USING columns to DataFusion, even for legacy JSON.
SELECT json_get(j, 'a') AS value
FROM (SELECT parse_json('{"a":"x"}') AS j) AS l
JOIN (SELECT parse_json('{"a":"x"}') AS j) AS r USING (j);
+-------+
| value |
+-------+
| x |
+-------+
-- Without USING, the unqualified column is genuinely ambiguous and must still fail.
SELECT json_get(j, 'a') AS value
FROM (SELECT parse_json('{"a":"x"}') AS j) AS l
JOIN (SELECT parse_json('{"a":"x"}') AS j) AS r ON l.j = r.j;
Error: 3000(PlanQuery), Failed to plan SQL: Ambiguous reference to unqualified field j
@@ -11,45 +11,139 @@ CREATE TABLE json2_query_respect_type_hint (
INSERT INTO json2_query_respect_type_hint VALUES
(1, '{"a":9007199254740993,"b":"three"}', 2),
(2, '{"a":7,"b":"7"}', 2);
(2, '{"a":7,"b":"7"}', 2),
(3, '{}', 2),
(4, '{"a":null,"b":null}', 2);
-- Both syntaxes use the hint when reading the memtable, including missing/null paths.
SELECT j.a AS dotted, json_get(j, 'a') AS direct,
arrow_typeof(j.a) AS dotted_type, arrow_typeof(json_get(j, 'a')) AS direct_type
FROM json2_query_respect_type_hint
ORDER BY ts;
-- An explicit third argument takes precedence over the BIGINT path hint in memtable reads.
SELECT json_get(j, 'a') AS hinted_bigint,
json_get(j, 'a', NULL::DOUBLE) AS explicit_double,
arrow_typeof(json_get(j, 'a')) AS hinted_type,
arrow_typeof(json_get(j, 'a', NULL::DOUBLE)) AS explicit_type
FROM json2_query_respect_type_hint
ORDER BY ts;
ADMIN FLUSH_TABLE('json2_query_respect_type_hint');
-- The explicit read type also overrides the hint in the plan and SST reads.
-- SQLNESS REPLACE (peers.*) REDACTED
EXPLAIN SELECT j.a AS hinted_bigint
EXPLAIN SELECT json_get(j, 'a', NULL::DOUBLE) AS explicit_double
FROM json2_query_respect_type_hint;
SELECT json_get(j, 'a') AS hinted_bigint,
json_get(j, 'a', NULL::DOUBLE) AS explicit_double,
arrow_typeof(json_get(j, 'a')) AS hinted_type,
arrow_typeof(json_get(j, 'a', NULL::DOUBLE)) AS explicit_type
FROM json2_query_respect_type_hint
ORDER BY ts;
-- SQLNESS REPLACE (peers.*) REDACTED
EXPLAIN SELECT j.a AS hinted_bigint, json_get(j, 'a') AS direct_bigint
FROM json2_query_respect_type_hint;
-- Uncast paths use their JSON2 type hints.
SELECT
j.a AS hinted_bigint,
j.b AS hinted_string
json_get(j, 'a') AS direct_bigint,
json_get(j, '$.a') AS rooted_bigint,
j.b AS hinted_string,
json_get(j, 'b') AS direct_string
FROM json2_query_respect_type_hint
ORDER BY ts;
-- The SST read keeps the same result types as the memtable read.
SELECT arrow_typeof(j.a) AS dotted_type, arrow_typeof(json_get(j, 'a')) AS direct_type,
arrow_typeof(j.b) AS dotted_string_type, arrow_typeof(json_get(j, 'b')) AS direct_string_type
FROM json2_query_respect_type_hint LIMIT 1;
-- A JSON2 type hint takes precedence over an explicit SQL cast for the native
-- read type.
-- SQLNESS REPLACE (peers.*) REDACTED
EXPLAIN SELECT j.a::STRING
EXPLAIN SELECT j.a::STRING, json_get(j, 'a')::STRING
FROM json2_query_respect_type_hint;
-- An explicit CAST changes the expression result type.
SELECT
j.a::STRING AS cast_string
j.a::STRING AS cast_string,
json_get(j, 'a')::STRING AS direct_cast_string,
arrow_typeof(json_get(j, 'a')::STRING) AS cast_type
FROM json2_query_respect_type_hint
ORDER BY ts;
-- A JSON2 type hint takes precedence over expression-context inference.
-- SQLNESS REPLACE (peers.*) REDACTED
EXPLAIN SELECT coalesce(j.b, exponent)
EXPLAIN SELECT coalesce(j.b, exponent), coalesce(json_get(j, 'b'), exponent)
FROM json2_query_respect_type_hint;
-- Hint injection precedes scalar, aggregate, window and binary-expression inference.
SELECT abs(j.a) AS dotted_abs, abs(json_get(j, 'a')) AS direct_abs,
j.a + 1 AS dotted_plus, json_get(j, 'a') + 1 AS direct_plus
FROM json2_query_respect_type_hint ORDER BY ts;
SELECT sum(j.a) AS dotted_sum, sum(json_get(j, 'a')) AS direct_sum
FROM json2_query_respect_type_hint;
SELECT sum(j.a) OVER (ORDER BY ts) AS dotted_sum,
sum(json_get(j, 'a')) OVER (ORDER BY ts) AS direct_sum
FROM json2_query_respect_type_hint ORDER BY ts;
-- A STRING type hint is respected, so numeric arithmetic requires an explicit cast.
SELECT j.b + exponent
FROM json2_query_respect_type_hint;
SELECT json_get(j, 'b') + exponent
FROM json2_query_respect_type_hint;
-- An explicit cast changes the expression type while preserving the STRING native read type.
-- The invalid string value still reports a normal SQL cast error.
SELECT j.b::DOUBLE + exponent
FROM json2_query_respect_type_hint;
SELECT json_get(j, 'b')::DOUBLE + exponent
FROM json2_query_respect_type_hint;
-- Resolve the input field through table aliases and a renamed derived-table column.
SELECT t.j.a AS dotted, json_get(t.j, 'a') AS direct
FROM json2_query_respect_type_hint AS t ORDER BY t.ts;
SELECT q.payload.a AS dotted, json_get(q.payload, 'a') AS direct
FROM (SELECT ts, j AS payload FROM json2_query_respect_type_hint) AS q ORDER BY q.ts;
DROP TABLE json2_query_respect_type_hint;
CREATE TABLE json2_hint_paths (
ts TIMESTAMP TIME INDEX,
j JSON2 (nested.a BIGINT, "nested.a" BOOLEAN)
) WITH (
'append_mode' = 'true'
);
INSERT INTO json2_hint_paths VALUES
(1, '{"nested":{"a":42},"nested.a":true,"unhinted":7}');
-- Quoted keys are literal names, not nested paths; unhinted paths retain their default type.
SELECT j.nested.a AS dotted_nested, json_get(j, '$.nested.a') AS direct_nested,
j."nested.a" AS dotted_key, json_get(j, '$."nested.a"') AS direct_key,
arrow_typeof(json_get(j, 'nested.a')) AS nested_type,
arrow_typeof(json_get(j, '$."nested.a"')) AS key_type,
arrow_typeof(j.unhinted) AS dotted_unhinted_type,
arrow_typeof(json_get(j, 'unhinted')) AS direct_unhinted_type
FROM json2_hint_paths;
DROP TABLE json2_hint_paths;
-- Hint lookup must defer unqualified JOIN USING columns to DataFusion, even for legacy JSON.
SELECT json_get(j, 'a') AS value
FROM (SELECT parse_json('{"a":"x"}') AS j) AS l
JOIN (SELECT parse_json('{"a":"x"}') AS j) AS r USING (j);
-- Without USING, the unqualified column is genuinely ambiguous and must still fail.
SELECT json_get(j, 'a') AS value
FROM (SELECT parse_json('{"a":"x"}') AS j) AS l
JOIN (SELECT parse_json('{"a":"x"}') AS j) AS r ON l.j = r.j;