mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2025-12-23 06:30:05 +00:00
Compare commits
41 Commits
transform-
...
build/add-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce0031067a | ||
|
|
c98db4f5ff | ||
|
|
975f19f452 | ||
|
|
d7c3c8e124 | ||
|
|
f4b9eac465 | ||
|
|
aa6c2de42a | ||
|
|
175fddb3b5 | ||
|
|
6afc4e778a | ||
|
|
3bbcde8e58 | ||
|
|
3bf9981aab | ||
|
|
c47ad548a4 | ||
|
|
0b6d78a527 | ||
|
|
d616bd92ef | ||
|
|
84aa5b7b22 | ||
|
|
cbf21e53a9 | ||
|
|
6248a6ccf5 | ||
|
|
0e0c4faf0d | ||
|
|
1a02fc31c2 | ||
|
|
8efbafa538 | ||
|
|
fcd0ceea94 | ||
|
|
22f31f5929 | ||
|
|
5d20acca44 | ||
|
|
e3733344fe | ||
|
|
305767e226 | ||
|
|
22a662f6bc | ||
|
|
1431393fc8 | ||
|
|
dfe8cf25f9 | ||
|
|
cccd25ddbb | ||
|
|
ac387bd2af | ||
|
|
2e9737c01d | ||
|
|
a8b426aebe | ||
|
|
f3509fa312 | ||
|
|
3dcd6b8e51 | ||
|
|
f221ee30fd | ||
|
|
fb822987a9 | ||
|
|
4ab6dc2825 | ||
|
|
191755fc42 | ||
|
|
1676d02149 | ||
|
|
edc49623de | ||
|
|
9405d1c578 | ||
|
|
7a4276c24a |
214
Cargo.lock
generated
214
Cargo.lock
generated
@@ -1041,7 +1041,7 @@ dependencies = [
|
|||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"cexpr",
|
"cexpr",
|
||||||
"clang-sys",
|
"clang-sys",
|
||||||
"itertools 0.12.1",
|
"itertools 0.10.5",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lazycell",
|
"lazycell",
|
||||||
"log",
|
"log",
|
||||||
@@ -1840,6 +1840,7 @@ dependencies = [
|
|||||||
"common-grpc",
|
"common-grpc",
|
||||||
"common-macro",
|
"common-macro",
|
||||||
"common-meta",
|
"common-meta",
|
||||||
|
"common-options",
|
||||||
"common-procedure",
|
"common-procedure",
|
||||||
"common-query",
|
"common-query",
|
||||||
"common-recordbatch",
|
"common-recordbatch",
|
||||||
@@ -1934,10 +1935,13 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"common-error",
|
"common-error",
|
||||||
"common-macro",
|
"common-macro",
|
||||||
|
"common-test-util",
|
||||||
"futures",
|
"futures",
|
||||||
"paste",
|
"paste",
|
||||||
|
"pin-project",
|
||||||
"serde",
|
"serde",
|
||||||
"snafu 0.8.5",
|
"snafu 0.8.5",
|
||||||
|
"tokio",
|
||||||
"toml 0.8.19",
|
"toml 0.8.19",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
@@ -2070,6 +2074,8 @@ dependencies = [
|
|||||||
"datafusion",
|
"datafusion",
|
||||||
"datatypes",
|
"datatypes",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
|
"geo",
|
||||||
|
"geo-types",
|
||||||
"geohash",
|
"geohash",
|
||||||
"h3o",
|
"h3o",
|
||||||
"jsonb",
|
"jsonb",
|
||||||
@@ -2088,6 +2094,7 @@ dependencies = [
|
|||||||
"store-api",
|
"store-api",
|
||||||
"table",
|
"table",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"wkt",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2236,6 +2243,15 @@ dependencies = [
|
|||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "common-options"
|
||||||
|
version = "0.9.5"
|
||||||
|
dependencies = [
|
||||||
|
"common-grpc",
|
||||||
|
"humantime-serde",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-plugins"
|
name = "common-plugins"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
@@ -3322,6 +3338,8 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"snafu 0.8.5",
|
"snafu 0.8.5",
|
||||||
|
"sqlparser 0.45.0 (git+https://github.com/GreptimeTeam/sqlparser-rs.git?rev=54a267ac89c09b11c0c88934690530807185d3e7)",
|
||||||
|
"sqlparser_derive 0.1.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3706,6 +3724,16 @@ version = "1.0.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
|
checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "earcutr"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79127ed59a85d7687c409e9978547cffb7dc79675355ed22da6b66fd5f6ead01"
|
||||||
|
dependencies = [
|
||||||
|
"itertools 0.11.0",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.13.0"
|
version = "1.13.0"
|
||||||
@@ -4014,6 +4042,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "float_next_after"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flow"
|
name = "flow"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
@@ -4154,6 +4188,7 @@ dependencies = [
|
|||||||
"common-grpc",
|
"common-grpc",
|
||||||
"common-macro",
|
"common-macro",
|
||||||
"common-meta",
|
"common-meta",
|
||||||
|
"common-options",
|
||||||
"common-procedure",
|
"common-procedure",
|
||||||
"common-query",
|
"common-query",
|
||||||
"common-recordbatch",
|
"common-recordbatch",
|
||||||
@@ -4438,6 +4473,24 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "geo"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "81d088357a9cc60cec8253b3578f6834b4a3aa20edb55f5d1c030c36d8143f11"
|
||||||
|
dependencies = [
|
||||||
|
"earcutr",
|
||||||
|
"float_next_after",
|
||||||
|
"geo-types",
|
||||||
|
"geographiclib-rs",
|
||||||
|
"i_overlay",
|
||||||
|
"log",
|
||||||
|
"num-traits",
|
||||||
|
"robust",
|
||||||
|
"rstar",
|
||||||
|
"spade",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "geo-types"
|
name = "geo-types"
|
||||||
version = "0.7.13"
|
version = "0.7.13"
|
||||||
@@ -4446,9 +4499,19 @@ checksum = "9ff16065e5720f376fbced200a5ae0f47ace85fd70b7e54269790281353b6d61"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"approx 0.5.1",
|
"approx 0.5.1",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"rstar",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "geographiclib-rs"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6e5ed84f8089c70234b0a8e0aedb6dc733671612ddc0d37c6066052f9781960"
|
||||||
|
dependencies = [
|
||||||
|
"libm",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "geohash"
|
name = "geohash"
|
||||||
version = "0.13.1"
|
version = "0.13.1"
|
||||||
@@ -4531,7 +4594,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "greptime-proto"
|
name = "greptime-proto"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/GreptimeTeam/greptime-proto.git?rev=255f87a3318ace3f88a67f76995a0e14910983f4#255f87a3318ace3f88a67f76995a0e14910983f4"
|
source = "git+https://github.com/GreptimeTeam/greptime-proto.git?rev=75c5fb569183bb3d0fa1023df9c2214df722b9b1#75c5fb569183bb3d0fa1023df9c2214df722b9b1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"prost 0.12.6",
|
"prost 0.12.6",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -4597,6 +4660,15 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hash32"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
@@ -4692,6 +4764,16 @@ dependencies = [
|
|||||||
"http 1.1.0",
|
"http 1.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heapless"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||||
|
dependencies = [
|
||||||
|
"hash32",
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@@ -5008,7 +5090,7 @@ dependencies = [
|
|||||||
"httpdate",
|
"httpdate",
|
||||||
"itoa",
|
"itoa",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2 0.5.7",
|
"socket2 0.4.10",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -5117,6 +5199,50 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "i_float"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f5fe043aae28ce70bd2f78b2f5f82a3654d63607c82594da4dabb8b6cb81f2b2"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "i_key_sort"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "347c253b4748a1a28baf94c9ce133b6b166f08573157e05afe718812bc599fcd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "i_overlay"
|
||||||
|
version = "1.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a469f68cb8a7cef375b2b0f581faf5859b4b50600438c00d46b71acc25ebbd0c"
|
||||||
|
dependencies = [
|
||||||
|
"i_float",
|
||||||
|
"i_key_sort",
|
||||||
|
"i_shape",
|
||||||
|
"i_tree",
|
||||||
|
"rayon",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "i_shape"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b44852d57a991c7dedaf76c55bc44f677f547ff899a430d29e13efd6133d7d8"
|
||||||
|
dependencies = [
|
||||||
|
"i_float",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "i_tree"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "155181bc97d770181cf9477da51218a19ee92a8e5be642e796661aee2b601139"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iana-time-zone"
|
name = "iana-time-zone"
|
||||||
version = "0.1.61"
|
version = "0.1.61"
|
||||||
@@ -5523,8 +5649,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonb"
|
name = "jsonb"
|
||||||
version = "0.4.1"
|
version = "0.4.3"
|
||||||
source = "git+https://github.com/databendlabs/jsonb.git?rev=46ad50fc71cf75afbf98eec455f7892a6387c1fc#46ad50fc71cf75afbf98eec455f7892a6387c1fc"
|
source = "git+https://github.com/databendlabs/jsonb.git?rev=8c8d2fc294a39f3ff08909d60f718639cfba3875#8c8d2fc294a39f3ff08909d60f718639cfba3875"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"fast-float",
|
"fast-float",
|
||||||
@@ -5551,6 +5677,19 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jsonpath-rust"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69a61b87f6a55cc6c28fed5739dd36b9642321ce63e4a5e4a4715d69106f4a10"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
"pest_derive",
|
||||||
|
"regex",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonptr"
|
name = "jsonptr"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
@@ -5661,7 +5800,7 @@ dependencies = [
|
|||||||
"hyper-rustls",
|
"hyper-rustls",
|
||||||
"hyper-timeout 0.5.1",
|
"hyper-timeout 0.5.1",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"jsonpath-rust",
|
"jsonpath-rust 0.5.1",
|
||||||
"k8s-openapi",
|
"k8s-openapi",
|
||||||
"kube-core",
|
"kube-core",
|
||||||
"pem 3.0.4",
|
"pem 3.0.4",
|
||||||
@@ -5941,7 +6080,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.48.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6345,6 +6484,7 @@ dependencies = [
|
|||||||
"common-telemetry",
|
"common-telemetry",
|
||||||
"datatypes",
|
"datatypes",
|
||||||
"futures",
|
"futures",
|
||||||
|
"futures-util",
|
||||||
"humantime-serde",
|
"humantime-serde",
|
||||||
"meta-srv",
|
"meta-srv",
|
||||||
"rand",
|
"rand",
|
||||||
@@ -6375,6 +6515,7 @@ dependencies = [
|
|||||||
"common-grpc",
|
"common-grpc",
|
||||||
"common-macro",
|
"common-macro",
|
||||||
"common-meta",
|
"common-meta",
|
||||||
|
"common-options",
|
||||||
"common-procedure",
|
"common-procedure",
|
||||||
"common-procedure-test",
|
"common-procedure-test",
|
||||||
"common-runtime",
|
"common-runtime",
|
||||||
@@ -6556,6 +6697,7 @@ dependencies = [
|
|||||||
"common-error",
|
"common-error",
|
||||||
"common-function",
|
"common-function",
|
||||||
"common-macro",
|
"common-macro",
|
||||||
|
"common-meta",
|
||||||
"common-procedure-test",
|
"common-procedure-test",
|
||||||
"common-query",
|
"common-query",
|
||||||
"common-recordbatch",
|
"common-recordbatch",
|
||||||
@@ -7596,6 +7738,7 @@ dependencies = [
|
|||||||
"file-engine",
|
"file-engine",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"jsonb",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"meta-client",
|
"meta-client",
|
||||||
"meter-core",
|
"meter-core",
|
||||||
@@ -8198,6 +8341,7 @@ dependencies = [
|
|||||||
"greptime-proto",
|
"greptime-proto",
|
||||||
"itertools 0.10.5",
|
"itertools 0.10.5",
|
||||||
"jsonb",
|
"jsonb",
|
||||||
|
"jsonpath-rust 0.7.3",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"moka",
|
"moka",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -8396,8 +8540,7 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "pprof"
|
name = "pprof"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/GreptimeTeam/pprof-rs?rev=1bd1e21#1bd1e210d8626da3d1e5aff976e6feee994f576d"
|
||||||
checksum = "ef5c97c51bd34c7e742402e216abdeb44d415fbe6ae41d56b114723e953711cb"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@@ -8679,7 +8822,7 @@ checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"itertools 0.12.1",
|
"itertools 0.10.5",
|
||||||
"log",
|
"log",
|
||||||
"multimap",
|
"multimap",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -8731,7 +8874,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
|
checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"itertools 0.12.1",
|
"itertools 0.10.5",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.79",
|
||||||
@@ -8847,6 +8990,8 @@ dependencies = [
|
|||||||
"auto_impl",
|
"auto_impl",
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
|
"bytes",
|
||||||
|
"common-base",
|
||||||
"common-error",
|
"common-error",
|
||||||
"common-macro",
|
"common-macro",
|
||||||
"common-runtime",
|
"common-runtime",
|
||||||
@@ -8891,7 +9036,7 @@ dependencies = [
|
|||||||
"indoc",
|
"indoc",
|
||||||
"libc",
|
"libc",
|
||||||
"memoffset 0.9.1",
|
"memoffset 0.9.1",
|
||||||
"parking_lot 0.12.3",
|
"parking_lot 0.11.2",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"pyo3-build-config",
|
"pyo3-build-config",
|
||||||
"pyo3-ffi",
|
"pyo3-ffi",
|
||||||
@@ -9568,6 +9713,12 @@ dependencies = [
|
|||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "robust"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cbf4a6aa5f6d6888f39e980649f3ad6b666acdce1d78e95b8a2cb076e687ae30"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ron"
|
name = "ron"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@@ -9662,6 +9813,17 @@ dependencies = [
|
|||||||
"zstd 0.13.2",
|
"zstd 0.13.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rstar"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "133315eb94c7b1e8d0cb097e5a710d850263372fd028fff18969de708afc7008"
|
||||||
|
dependencies = [
|
||||||
|
"heapless",
|
||||||
|
"num-traits",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rstest"
|
name = "rstest"
|
||||||
version = "0.21.0"
|
version = "0.21.0"
|
||||||
@@ -11154,6 +11316,18 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spade"
|
||||||
|
version = "2.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93f5ef1f863aca7d1d7dda7ccfc36a0a4279bd6d3c375176e5e0712e25cb4889"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown 0.14.5",
|
||||||
|
"num-traits",
|
||||||
|
"robust",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sparsevec"
|
name = "sparsevec"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -11500,6 +11674,7 @@ dependencies = [
|
|||||||
"derive_builder 0.12.0",
|
"derive_builder 0.12.0",
|
||||||
"futures",
|
"futures",
|
||||||
"humantime",
|
"humantime",
|
||||||
|
"prost 0.12.6",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"snafu 0.8.5",
|
"snafu 0.8.5",
|
||||||
@@ -12196,6 +12371,7 @@ dependencies = [
|
|||||||
"frontend",
|
"frontend",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"hex",
|
||||||
"itertools 0.10.5",
|
"itertools 0.10.5",
|
||||||
"meta-client",
|
"meta-client",
|
||||||
"meta-srv",
|
"meta-srv",
|
||||||
@@ -13805,7 +13981,7 @@ version = "0.1.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -14147,6 +14323,18 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wkt"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "54f7f1ff4ea4c18936d6cd26a6fd24f0003af37e951a8e0e8b9e9a2d0bd0a46d"
|
||||||
|
dependencies = [
|
||||||
|
"geo-types",
|
||||||
|
"log",
|
||||||
|
"num-traits",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wyz"
|
name = "wyz"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
|||||||
25
Cargo.toml
25
Cargo.toml
@@ -2,23 +2,25 @@
|
|||||||
members = [
|
members = [
|
||||||
"src/api",
|
"src/api",
|
||||||
"src/auth",
|
"src/auth",
|
||||||
"src/catalog",
|
|
||||||
"src/cache",
|
"src/cache",
|
||||||
|
"src/catalog",
|
||||||
"src/client",
|
"src/client",
|
||||||
"src/cmd",
|
"src/cmd",
|
||||||
"src/common/base",
|
"src/common/base",
|
||||||
"src/common/catalog",
|
"src/common/catalog",
|
||||||
"src/common/config",
|
"src/common/config",
|
||||||
"src/common/datasource",
|
"src/common/datasource",
|
||||||
|
"src/common/decimal",
|
||||||
"src/common/error",
|
"src/common/error",
|
||||||
"src/common/frontend",
|
"src/common/frontend",
|
||||||
"src/common/function",
|
"src/common/function",
|
||||||
"src/common/macro",
|
|
||||||
"src/common/greptimedb-telemetry",
|
"src/common/greptimedb-telemetry",
|
||||||
"src/common/grpc",
|
"src/common/grpc",
|
||||||
"src/common/grpc-expr",
|
"src/common/grpc-expr",
|
||||||
|
"src/common/macro",
|
||||||
"src/common/mem-prof",
|
"src/common/mem-prof",
|
||||||
"src/common/meta",
|
"src/common/meta",
|
||||||
|
"src/common/options",
|
||||||
"src/common/plugins",
|
"src/common/plugins",
|
||||||
"src/common/pprof",
|
"src/common/pprof",
|
||||||
"src/common/procedure",
|
"src/common/procedure",
|
||||||
@@ -30,7 +32,6 @@ members = [
|
|||||||
"src/common/telemetry",
|
"src/common/telemetry",
|
||||||
"src/common/test-util",
|
"src/common/test-util",
|
||||||
"src/common/time",
|
"src/common/time",
|
||||||
"src/common/decimal",
|
|
||||||
"src/common/version",
|
"src/common/version",
|
||||||
"src/common/wal",
|
"src/common/wal",
|
||||||
"src/datanode",
|
"src/datanode",
|
||||||
@@ -38,6 +39,7 @@ members = [
|
|||||||
"src/file-engine",
|
"src/file-engine",
|
||||||
"src/flow",
|
"src/flow",
|
||||||
"src/frontend",
|
"src/frontend",
|
||||||
|
"src/index",
|
||||||
"src/log-store",
|
"src/log-store",
|
||||||
"src/meta-client",
|
"src/meta-client",
|
||||||
"src/meta-srv",
|
"src/meta-srv",
|
||||||
@@ -57,7 +59,6 @@ members = [
|
|||||||
"src/sql",
|
"src/sql",
|
||||||
"src/store-api",
|
"src/store-api",
|
||||||
"src/table",
|
"src/table",
|
||||||
"src/index",
|
|
||||||
"tests-fuzz",
|
"tests-fuzz",
|
||||||
"tests-integration",
|
"tests-integration",
|
||||||
"tests/runner",
|
"tests/runner",
|
||||||
@@ -117,15 +118,16 @@ datafusion-sql = { git = "https://github.com/waynexia/arrow-datafusion.git", rev
|
|||||||
datafusion-substrait = { git = "https://github.com/waynexia/arrow-datafusion.git", rev = "7823ef2f63663907edab46af0d51359900f608d6" }
|
datafusion-substrait = { git = "https://github.com/waynexia/arrow-datafusion.git", rev = "7823ef2f63663907edab46af0d51359900f608d6" }
|
||||||
derive_builder = "0.12"
|
derive_builder = "0.12"
|
||||||
dotenv = "0.15"
|
dotenv = "0.15"
|
||||||
etcd-client = { version = "0.13" }
|
etcd-client = "0.13"
|
||||||
fst = "0.4.7"
|
fst = "0.4.7"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
greptime-proto = { git = "https://github.com/GreptimeTeam/greptime-proto.git", rev = "255f87a3318ace3f88a67f76995a0e14910983f4" }
|
greptime-proto = { git = "https://github.com/GreptimeTeam/greptime-proto.git", rev = "75c5fb569183bb3d0fa1023df9c2214df722b9b1" }
|
||||||
|
hex = "0.4"
|
||||||
humantime = "2.1"
|
humantime = "2.1"
|
||||||
humantime-serde = "1.1"
|
humantime-serde = "1.1"
|
||||||
itertools = "0.10"
|
itertools = "0.10"
|
||||||
jsonb = { git = "https://github.com/databendlabs/jsonb.git", rev = "46ad50fc71cf75afbf98eec455f7892a6387c1fc", default-features = false }
|
jsonb = { git = "https://github.com/databendlabs/jsonb.git", rev = "8c8d2fc294a39f3ff08909d60f718639cfba3875", default-features = false }
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
meter-core = { git = "https://github.com/GreptimeTeam/greptime-meter.git", rev = "a10facb353b41460eeb98578868ebf19c2084fac" }
|
meter-core = { git = "https://github.com/GreptimeTeam/greptime-meter.git", rev = "a10facb353b41460eeb98578868ebf19c2084fac" }
|
||||||
mockall = "0.11.4"
|
mockall = "0.11.4"
|
||||||
@@ -151,7 +153,7 @@ raft-engine = { version = "0.4.1", default-features = false }
|
|||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
ratelimit = "0.9"
|
ratelimit = "0.9"
|
||||||
regex = "1.8"
|
regex = "1.8"
|
||||||
regex-automata = { version = "0.4" }
|
regex-automata = "0.4"
|
||||||
reqwest = { version = "0.12", default-features = false, features = [
|
reqwest = { version = "0.12", default-features = false, features = [
|
||||||
"json",
|
"json",
|
||||||
"rustls-tls-native-roots",
|
"rustls-tls-native-roots",
|
||||||
@@ -182,11 +184,11 @@ strum = { version = "0.25", features = ["derive"] }
|
|||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
tokio = { version = "1.40", features = ["full"] }
|
tokio = { version = "1.40", features = ["full"] }
|
||||||
tokio-postgres = "0.7"
|
tokio-postgres = "0.7"
|
||||||
tokio-stream = { version = "0.1" }
|
tokio-stream = "0.1"
|
||||||
tokio-util = { version = "0.7", features = ["io-util", "compat"] }
|
tokio-util = { version = "0.7", features = ["io-util", "compat"] }
|
||||||
toml = "0.8.8"
|
toml = "0.8.8"
|
||||||
tonic = { version = "0.11", features = ["tls", "gzip", "zstd"] }
|
tonic = { version = "0.11", features = ["tls", "gzip", "zstd"] }
|
||||||
tower = { version = "0.4" }
|
tower = "0.4"
|
||||||
tracing-appender = "0.2"
|
tracing-appender = "0.2"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "json", "fmt"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter", "json", "fmt"] }
|
||||||
typetag = "0.2"
|
typetag = "0.2"
|
||||||
@@ -214,6 +216,7 @@ common-grpc-expr = { path = "src/common/grpc-expr" }
|
|||||||
common-macro = { path = "src/common/macro" }
|
common-macro = { path = "src/common/macro" }
|
||||||
common-mem-prof = { path = "src/common/mem-prof" }
|
common-mem-prof = { path = "src/common/mem-prof" }
|
||||||
common-meta = { path = "src/common/meta" }
|
common-meta = { path = "src/common/meta" }
|
||||||
|
common-options = { path = "src/common/options" }
|
||||||
common-plugins = { path = "src/common/plugins" }
|
common-plugins = { path = "src/common/plugins" }
|
||||||
common-pprof = { path = "src/common/pprof" }
|
common-pprof = { path = "src/common/pprof" }
|
||||||
common-procedure = { path = "src/common/procedure" }
|
common-procedure = { path = "src/common/procedure" }
|
||||||
@@ -261,6 +264,8 @@ tokio-rustls = { git = "https://github.com/GreptimeTeam/tokio-rustls" }
|
|||||||
# This is commented, since we are not using aws-lc-sys, if we need to use it, we need to uncomment this line or use a release after this commit, or it wouldn't compile with gcc < 8.1
|
# This is commented, since we are not using aws-lc-sys, if we need to use it, we need to uncomment this line or use a release after this commit, or it wouldn't compile with gcc < 8.1
|
||||||
# see https://github.com/aws/aws-lc-rs/pull/526
|
# see https://github.com/aws/aws-lc-rs/pull/526
|
||||||
# aws-lc-sys = { git ="https://github.com/aws/aws-lc-rs", rev = "556558441e3494af4b156ae95ebc07ebc2fd38aa" }
|
# aws-lc-sys = { git ="https://github.com/aws/aws-lc-rs", rev = "556558441e3494af4b156ae95ebc07ebc2fd38aa" }
|
||||||
|
# Apply a fix for pprof for unaligned pointer access
|
||||||
|
pprof = { git = "https://github.com/GreptimeTeam/pprof-rs", rev = "1bd1e21" }
|
||||||
|
|
||||||
[workspace.dependencies.meter-macros]
|
[workspace.dependencies.meter-macros]
|
||||||
git = "https://github.com/GreptimeTeam/greptime-meter.git"
|
git = "https://github.com/GreptimeTeam/greptime-meter.git"
|
||||||
|
|||||||
16
README.md
16
README.md
@@ -6,7 +6,7 @@
|
|||||||
</picture>
|
</picture>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 align="center">Unified Time Series Database for Metrics, Logs, and Events</h2>
|
<h2 align="center">Unified & Cost-Effective Time Series Database for Metrics, Logs, and Events</h2>
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<h3 align="center">
|
<h3 align="center">
|
||||||
@@ -48,9 +48,21 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
- [Introduction](#introduction)
|
||||||
|
- [**Features: Why GreptimeDB**](#why-greptimedb)
|
||||||
|
- [Architecture](https://docs.greptime.com/contributor-guide/overview/#architecture)
|
||||||
|
- [Try it for free](#try-greptimedb)
|
||||||
|
- [Getting Started](#getting-started)
|
||||||
|
- [Project Status](#project-status)
|
||||||
|
- [Join the community](#community)
|
||||||
|
- [Contributing](#contributing)
|
||||||
|
- [Extension](#extension )
|
||||||
|
- [License](#license)
|
||||||
|
- [Acknowledgement](#acknowledgement)
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
**GreptimeDB** is an open-source unified time-series database for **Metrics**, **Logs**, and **Events** (also **Traces** in plan). You can gain real-time insights from Edge to Cloud at any scale.
|
**GreptimeDB** is an open-source unified & cost-effective time-series database for **Metrics**, **Logs**, and **Events** (also **Traces** in plan). You can gain real-time insights from Edge to Cloud at Any Scale.
|
||||||
|
|
||||||
## Why GreptimeDB
|
## Why GreptimeDB
|
||||||
|
|
||||||
|
|||||||
@@ -93,8 +93,8 @@
|
|||||||
| `storage` | -- | -- | The data storage options. |
|
| `storage` | -- | -- | The data storage options. |
|
||||||
| `storage.data_home` | String | `/tmp/greptimedb/` | The working home directory. |
|
| `storage.data_home` | String | `/tmp/greptimedb/` | The working home directory. |
|
||||||
| `storage.type` | String | `File` | The storage type used to store the data.<br/>- `File`: the data is stored in the local file system.<br/>- `S3`: the data is stored in the S3 object storage.<br/>- `Gcs`: the data is stored in the Google Cloud Storage.<br/>- `Azblob`: the data is stored in the Azure Blob Storage.<br/>- `Oss`: the data is stored in the Aliyun OSS. |
|
| `storage.type` | String | `File` | The storage type used to store the data.<br/>- `File`: the data is stored in the local file system.<br/>- `S3`: the data is stored in the S3 object storage.<br/>- `Gcs`: the data is stored in the Google Cloud Storage.<br/>- `Azblob`: the data is stored in the Azure Blob Storage.<br/>- `Oss`: the data is stored in the Aliyun OSS. |
|
||||||
| `storage.cache_path` | String | Unset | Cache configuration for object storage such as 'S3' etc.<br/>The local file cache directory. |
|
| `storage.cache_path` | String | Unset | Cache configuration for object storage such as 'S3' etc. It is recommended to configure it when using object storage for better performance.<br/>The local file cache directory. |
|
||||||
| `storage.cache_capacity` | String | Unset | The local file cache capacity in bytes. |
|
| `storage.cache_capacity` | String | Unset | The local file cache capacity in bytes. If your disk space is sufficient, it is recommended to set it larger. |
|
||||||
| `storage.bucket` | String | Unset | The S3 bucket name.<br/>**It's only used when the storage type is `S3`, `Oss` and `Gcs`**. |
|
| `storage.bucket` | String | Unset | The S3 bucket name.<br/>**It's only used when the storage type is `S3`, `Oss` and `Gcs`**. |
|
||||||
| `storage.root` | String | Unset | The S3 data will be stored in the specified prefix, for example, `s3://${bucket}/${root}`.<br/>**It's only used when the storage type is `S3`, `Oss` and `Azblob`**. |
|
| `storage.root` | String | Unset | The S3 data will be stored in the specified prefix, for example, `s3://${bucket}/${root}`.<br/>**It's only used when the storage type is `S3`, `Oss` and `Azblob`**. |
|
||||||
| `storage.access_key_id` | String | Unset | The access key id of the aws account.<br/>It's **highly recommended** to use AWS IAM roles instead of hardcoding the access key id and secret key.<br/>**It's only used when the storage type is `S3` and `Oss`**. |
|
| `storage.access_key_id` | String | Unset | The access key id of the aws account.<br/>It's **highly recommended** to use AWS IAM roles instead of hardcoding the access key id and secret key.<br/>**It's only used when the storage type is `S3` and `Oss`**. |
|
||||||
@@ -126,9 +126,9 @@
|
|||||||
| `region_engine.mito.vector_cache_size` | String | Auto | Cache size for vectors and arrow arrays. Setting it to 0 to disable the cache.<br/>If not set, it's default to 1/16 of OS memory with a max limitation of 512MB. |
|
| `region_engine.mito.vector_cache_size` | String | Auto | Cache size for vectors and arrow arrays. Setting it to 0 to disable the cache.<br/>If not set, it's default to 1/16 of OS memory with a max limitation of 512MB. |
|
||||||
| `region_engine.mito.page_cache_size` | String | Auto | Cache size for pages of SST row groups. Setting it to 0 to disable the cache.<br/>If not set, it's default to 1/8 of OS memory. |
|
| `region_engine.mito.page_cache_size` | String | Auto | Cache size for pages of SST row groups. Setting it to 0 to disable the cache.<br/>If not set, it's default to 1/8 of OS memory. |
|
||||||
| `region_engine.mito.selector_result_cache_size` | String | Auto | Cache size for time series selector (e.g. `last_value()`). Setting it to 0 to disable the cache.<br/>If not set, it's default to 1/16 of OS memory with a max limitation of 512MB. |
|
| `region_engine.mito.selector_result_cache_size` | String | Auto | Cache size for time series selector (e.g. `last_value()`). Setting it to 0 to disable the cache.<br/>If not set, it's default to 1/16 of OS memory with a max limitation of 512MB. |
|
||||||
| `region_engine.mito.enable_experimental_write_cache` | Bool | `false` | Whether to enable the experimental write cache. |
|
| `region_engine.mito.enable_experimental_write_cache` | Bool | `false` | Whether to enable the experimental write cache. It is recommended to enable it when using object storage for better performance. |
|
||||||
| `region_engine.mito.experimental_write_cache_path` | String | `""` | File system path for write cache, defaults to `{data_home}/write_cache`. |
|
| `region_engine.mito.experimental_write_cache_path` | String | `""` | File system path for write cache, defaults to `{data_home}/write_cache`. |
|
||||||
| `region_engine.mito.experimental_write_cache_size` | String | `512MB` | Capacity for write cache. |
|
| `region_engine.mito.experimental_write_cache_size` | String | `1GiB` | Capacity for write cache. If your disk space is sufficient, it is recommended to set it larger. |
|
||||||
| `region_engine.mito.experimental_write_cache_ttl` | String | Unset | TTL for write cache. |
|
| `region_engine.mito.experimental_write_cache_ttl` | String | Unset | TTL for write cache. |
|
||||||
| `region_engine.mito.sst_write_buffer_size` | String | `8MB` | Buffer size for SST writing. |
|
| `region_engine.mito.sst_write_buffer_size` | String | `8MB` | Buffer size for SST writing. |
|
||||||
| `region_engine.mito.scan_parallelism` | Integer | `0` | Parallelism to scan a region (default: 1/4 of cpu cores).<br/>- `0`: using the default value (1/4 of cpu cores).<br/>- `1`: scan in current thread.<br/>- `n`: scan in parallelism n. |
|
| `region_engine.mito.scan_parallelism` | Integer | `0` | Parallelism to scan a region (default: 1/4 of cpu cores).<br/>- `0`: using the default value (1/4 of cpu cores).<br/>- `1`: scan in current thread.<br/>- `n`: scan in parallelism n. |
|
||||||
@@ -416,8 +416,8 @@
|
|||||||
| `storage` | -- | -- | The data storage options. |
|
| `storage` | -- | -- | The data storage options. |
|
||||||
| `storage.data_home` | String | `/tmp/greptimedb/` | The working home directory. |
|
| `storage.data_home` | String | `/tmp/greptimedb/` | The working home directory. |
|
||||||
| `storage.type` | String | `File` | The storage type used to store the data.<br/>- `File`: the data is stored in the local file system.<br/>- `S3`: the data is stored in the S3 object storage.<br/>- `Gcs`: the data is stored in the Google Cloud Storage.<br/>- `Azblob`: the data is stored in the Azure Blob Storage.<br/>- `Oss`: the data is stored in the Aliyun OSS. |
|
| `storage.type` | String | `File` | The storage type used to store the data.<br/>- `File`: the data is stored in the local file system.<br/>- `S3`: the data is stored in the S3 object storage.<br/>- `Gcs`: the data is stored in the Google Cloud Storage.<br/>- `Azblob`: the data is stored in the Azure Blob Storage.<br/>- `Oss`: the data is stored in the Aliyun OSS. |
|
||||||
| `storage.cache_path` | String | Unset | Cache configuration for object storage such as 'S3' etc.<br/>The local file cache directory. |
|
| `storage.cache_path` | String | Unset | Cache configuration for object storage such as 'S3' etc. It is recommended to configure it when using object storage for better performance.<br/>The local file cache directory. |
|
||||||
| `storage.cache_capacity` | String | Unset | The local file cache capacity in bytes. |
|
| `storage.cache_capacity` | String | Unset | The local file cache capacity in bytes. If your disk space is sufficient, it is recommended to set it larger. |
|
||||||
| `storage.bucket` | String | Unset | The S3 bucket name.<br/>**It's only used when the storage type is `S3`, `Oss` and `Gcs`**. |
|
| `storage.bucket` | String | Unset | The S3 bucket name.<br/>**It's only used when the storage type is `S3`, `Oss` and `Gcs`**. |
|
||||||
| `storage.root` | String | Unset | The S3 data will be stored in the specified prefix, for example, `s3://${bucket}/${root}`.<br/>**It's only used when the storage type is `S3`, `Oss` and `Azblob`**. |
|
| `storage.root` | String | Unset | The S3 data will be stored in the specified prefix, for example, `s3://${bucket}/${root}`.<br/>**It's only used when the storage type is `S3`, `Oss` and `Azblob`**. |
|
||||||
| `storage.access_key_id` | String | Unset | The access key id of the aws account.<br/>It's **highly recommended** to use AWS IAM roles instead of hardcoding the access key id and secret key.<br/>**It's only used when the storage type is `S3` and `Oss`**. |
|
| `storage.access_key_id` | String | Unset | The access key id of the aws account.<br/>It's **highly recommended** to use AWS IAM roles instead of hardcoding the access key id and secret key.<br/>**It's only used when the storage type is `S3` and `Oss`**. |
|
||||||
@@ -449,9 +449,9 @@
|
|||||||
| `region_engine.mito.vector_cache_size` | String | Auto | Cache size for vectors and arrow arrays. Setting it to 0 to disable the cache.<br/>If not set, it's default to 1/16 of OS memory with a max limitation of 512MB. |
|
| `region_engine.mito.vector_cache_size` | String | Auto | Cache size for vectors and arrow arrays. Setting it to 0 to disable the cache.<br/>If not set, it's default to 1/16 of OS memory with a max limitation of 512MB. |
|
||||||
| `region_engine.mito.page_cache_size` | String | Auto | Cache size for pages of SST row groups. Setting it to 0 to disable the cache.<br/>If not set, it's default to 1/8 of OS memory. |
|
| `region_engine.mito.page_cache_size` | String | Auto | Cache size for pages of SST row groups. Setting it to 0 to disable the cache.<br/>If not set, it's default to 1/8 of OS memory. |
|
||||||
| `region_engine.mito.selector_result_cache_size` | String | Auto | Cache size for time series selector (e.g. `last_value()`). Setting it to 0 to disable the cache.<br/>If not set, it's default to 1/16 of OS memory with a max limitation of 512MB. |
|
| `region_engine.mito.selector_result_cache_size` | String | Auto | Cache size for time series selector (e.g. `last_value()`). Setting it to 0 to disable the cache.<br/>If not set, it's default to 1/16 of OS memory with a max limitation of 512MB. |
|
||||||
| `region_engine.mito.enable_experimental_write_cache` | Bool | `false` | Whether to enable the experimental write cache. |
|
| `region_engine.mito.enable_experimental_write_cache` | Bool | `false` | Whether to enable the experimental write cache. It is recommended to enable it when using object storage for better performance. |
|
||||||
| `region_engine.mito.experimental_write_cache_path` | String | `""` | File system path for write cache, defaults to `{data_home}/write_cache`. |
|
| `region_engine.mito.experimental_write_cache_path` | String | `""` | File system path for write cache, defaults to `{data_home}/write_cache`. |
|
||||||
| `region_engine.mito.experimental_write_cache_size` | String | `512MB` | Capacity for write cache. |
|
| `region_engine.mito.experimental_write_cache_size` | String | `1GiB` | Capacity for write cache. If your disk space is sufficient, it is recommended to set it larger. |
|
||||||
| `region_engine.mito.experimental_write_cache_ttl` | String | Unset | TTL for write cache. |
|
| `region_engine.mito.experimental_write_cache_ttl` | String | Unset | TTL for write cache. |
|
||||||
| `region_engine.mito.sst_write_buffer_size` | String | `8MB` | Buffer size for SST writing. |
|
| `region_engine.mito.sst_write_buffer_size` | String | `8MB` | Buffer size for SST writing. |
|
||||||
| `region_engine.mito.scan_parallelism` | Integer | `0` | Parallelism to scan a region (default: 1/4 of cpu cores).<br/>- `0`: using the default value (1/4 of cpu cores).<br/>- `1`: scan in current thread.<br/>- `n`: scan in parallelism n. |
|
| `region_engine.mito.scan_parallelism` | Integer | `0` | Parallelism to scan a region (default: 1/4 of cpu cores).<br/>- `0`: using the default value (1/4 of cpu cores).<br/>- `1`: scan in current thread.<br/>- `n`: scan in parallelism n. |
|
||||||
|
|||||||
@@ -294,14 +294,14 @@ data_home = "/tmp/greptimedb/"
|
|||||||
## - `Oss`: the data is stored in the Aliyun OSS.
|
## - `Oss`: the data is stored in the Aliyun OSS.
|
||||||
type = "File"
|
type = "File"
|
||||||
|
|
||||||
## Cache configuration for object storage such as 'S3' etc.
|
## Cache configuration for object storage such as 'S3' etc. It is recommended to configure it when using object storage for better performance.
|
||||||
## The local file cache directory.
|
## The local file cache directory.
|
||||||
## @toml2docs:none-default
|
## @toml2docs:none-default
|
||||||
cache_path = "/path/local_cache"
|
cache_path = "/path/local_cache"
|
||||||
|
|
||||||
## The local file cache capacity in bytes.
|
## The local file cache capacity in bytes. If your disk space is sufficient, it is recommended to set it larger.
|
||||||
## @toml2docs:none-default
|
## @toml2docs:none-default
|
||||||
cache_capacity = "256MB"
|
cache_capacity = "1GiB"
|
||||||
|
|
||||||
## The S3 bucket name.
|
## The S3 bucket name.
|
||||||
## **It's only used when the storage type is `S3`, `Oss` and `Gcs`**.
|
## **It's only used when the storage type is `S3`, `Oss` and `Gcs`**.
|
||||||
@@ -459,14 +459,14 @@ auto_flush_interval = "1h"
|
|||||||
## @toml2docs:none-default="Auto"
|
## @toml2docs:none-default="Auto"
|
||||||
#+ selector_result_cache_size = "512MB"
|
#+ selector_result_cache_size = "512MB"
|
||||||
|
|
||||||
## Whether to enable the experimental write cache.
|
## Whether to enable the experimental write cache. It is recommended to enable it when using object storage for better performance.
|
||||||
enable_experimental_write_cache = false
|
enable_experimental_write_cache = false
|
||||||
|
|
||||||
## File system path for write cache, defaults to `{data_home}/write_cache`.
|
## File system path for write cache, defaults to `{data_home}/write_cache`.
|
||||||
experimental_write_cache_path = ""
|
experimental_write_cache_path = ""
|
||||||
|
|
||||||
## Capacity for write cache.
|
## Capacity for write cache. If your disk space is sufficient, it is recommended to set it larger.
|
||||||
experimental_write_cache_size = "512MB"
|
experimental_write_cache_size = "1GiB"
|
||||||
|
|
||||||
## TTL for write cache.
|
## TTL for write cache.
|
||||||
## @toml2docs:none-default
|
## @toml2docs:none-default
|
||||||
|
|||||||
@@ -332,14 +332,14 @@ data_home = "/tmp/greptimedb/"
|
|||||||
## - `Oss`: the data is stored in the Aliyun OSS.
|
## - `Oss`: the data is stored in the Aliyun OSS.
|
||||||
type = "File"
|
type = "File"
|
||||||
|
|
||||||
## Cache configuration for object storage such as 'S3' etc.
|
## Cache configuration for object storage such as 'S3' etc. It is recommended to configure it when using object storage for better performance.
|
||||||
## The local file cache directory.
|
## The local file cache directory.
|
||||||
## @toml2docs:none-default
|
## @toml2docs:none-default
|
||||||
cache_path = "/path/local_cache"
|
cache_path = "/path/local_cache"
|
||||||
|
|
||||||
## The local file cache capacity in bytes.
|
## The local file cache capacity in bytes. If your disk space is sufficient, it is recommended to set it larger.
|
||||||
## @toml2docs:none-default
|
## @toml2docs:none-default
|
||||||
cache_capacity = "256MB"
|
cache_capacity = "1GiB"
|
||||||
|
|
||||||
## The S3 bucket name.
|
## The S3 bucket name.
|
||||||
## **It's only used when the storage type is `S3`, `Oss` and `Gcs`**.
|
## **It's only used when the storage type is `S3`, `Oss` and `Gcs`**.
|
||||||
@@ -497,14 +497,14 @@ auto_flush_interval = "1h"
|
|||||||
## @toml2docs:none-default="Auto"
|
## @toml2docs:none-default="Auto"
|
||||||
#+ selector_result_cache_size = "512MB"
|
#+ selector_result_cache_size = "512MB"
|
||||||
|
|
||||||
## Whether to enable the experimental write cache.
|
## Whether to enable the experimental write cache. It is recommended to enable it when using object storage for better performance.
|
||||||
enable_experimental_write_cache = false
|
enable_experimental_write_cache = false
|
||||||
|
|
||||||
## File system path for write cache, defaults to `{data_home}/write_cache`.
|
## File system path for write cache, defaults to `{data_home}/write_cache`.
|
||||||
experimental_write_cache_path = ""
|
experimental_write_cache_path = ""
|
||||||
|
|
||||||
## Capacity for write cache.
|
## Capacity for write cache. If your disk space is sufficient, it is recommended to set it larger.
|
||||||
experimental_write_cache_size = "512MB"
|
experimental_write_cache_size = "1GiB"
|
||||||
|
|
||||||
## TTL for write cache.
|
## TTL for write cache.
|
||||||
## @toml2docs:none-default
|
## @toml2docs:none-default
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ RUN yum install -y epel-release \
|
|||||||
|
|
||||||
WORKDIR /greptime
|
WORKDIR /greptime
|
||||||
COPY --from=builder /out/target/${OUTPUT_DIR}/greptime /greptime/bin/
|
COPY --from=builder /out/target/${OUTPUT_DIR}/greptime /greptime/bin/
|
||||||
|
COPY --from=builder /out/target/${OUTPUT_DIR}/build/tikv-jemalloc-sys-*/out/build/bin/jeprof /greptime/bin/
|
||||||
ENV PATH /greptime/bin/:$PATH
|
ENV PATH /greptime/bin/:$PATH
|
||||||
|
|
||||||
ENTRYPOINT ["greptime"]
|
ENTRYPOINT ["greptime"]
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ RUN python3 -m pip install -r /etc/greptime/requirements.txt
|
|||||||
|
|
||||||
WORKDIR /greptime
|
WORKDIR /greptime
|
||||||
COPY --from=builder /out/target/${OUTPUT_DIR}/greptime /greptime/bin/
|
COPY --from=builder /out/target/${OUTPUT_DIR}/greptime /greptime/bin/
|
||||||
|
COPY --from=builder /out/target/${OUTPUT_DIR}/build/tikv-jemalloc-sys-*/out/build/bin/jeprof /greptime/bin/
|
||||||
|
RUN chmod +x /greptime/bin/jeprof
|
||||||
|
|
||||||
ENV PATH /greptime/bin/:$PATH
|
ENV PATH /greptime/bin/:$PATH
|
||||||
|
|
||||||
ENTRYPOINT ["greptime"]
|
ENTRYPOINT ["greptime"]
|
||||||
|
|||||||
@@ -4,13 +4,13 @@
|
|||||||
|
|
||||||
example:
|
example:
|
||||||
```bash
|
```bash
|
||||||
curl --data "trace;flow=debug" 127.0.0.1:4000/debug/log_level
|
curl --data "trace,flow=debug" 127.0.0.1:4000/debug/log_level
|
||||||
```
|
```
|
||||||
And database will reply with something like:
|
And database will reply with something like:
|
||||||
```bash
|
```bash
|
||||||
Log Level changed from Some("info") to "trace;flow=debug"%
|
Log Level changed from Some("info") to "trace,flow=debug"%
|
||||||
```
|
```
|
||||||
|
|
||||||
The data is a string in the format of `global_level;module1=level1;module2=level2;...` that follow the same rule of `RUST_LOG`.
|
The data is a string in the format of `global_level,module1=level1,module2=level2,...` that follow the same rule of `RUST_LOG`.
|
||||||
|
|
||||||
The module is the module name of the log, and the level is the log level. The log level can be one of the following: `trace`, `debug`, `info`, `warn`, `error`, `off`(case insensitive).
|
The module is the module name of the log, and the level is the log level. The log level can be one of the following: `trace`, `debug`, `info`, `warn`, `error`, `off`(case insensitive).
|
||||||
@@ -4,6 +4,18 @@ This crate provides an easy approach to dump memory profiling info.
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
### jemalloc
|
### jemalloc
|
||||||
|
jeprof is already compiled in the target directory of GreptimeDB. You can find the binary and use it.
|
||||||
|
```
|
||||||
|
# find jeprof binary
|
||||||
|
find . -name 'jeprof'
|
||||||
|
|
||||||
|
# add executable permission
|
||||||
|
chmod +x <path_to_jeprof>
|
||||||
|
```
|
||||||
|
The path is usually under `./target/${PROFILE}/build/tikv-jemalloc-sys-${HASH}/out/build/bin/jeprof`.
|
||||||
|
|
||||||
|
The default version of jemalloc installed from the package manager may not have the `--collapsed` option.
|
||||||
|
You may need to check the whether the `jeprof` version is >= `5.3.0` if you want to install it from the package manager.
|
||||||
```bash
|
```bash
|
||||||
# for macOS
|
# for macOS
|
||||||
brew install jemalloc
|
brew install jemalloc
|
||||||
|
|||||||
@@ -36,15 +36,14 @@ use datatypes::vectors::{
|
|||||||
TimestampMillisecondVector, TimestampNanosecondVector, TimestampSecondVector, UInt32Vector,
|
TimestampMillisecondVector, TimestampNanosecondVector, TimestampSecondVector, UInt32Vector,
|
||||||
UInt64Vector, VectorRef,
|
UInt64Vector, VectorRef,
|
||||||
};
|
};
|
||||||
use greptime_proto::v1;
|
|
||||||
use greptime_proto::v1::column_data_type_extension::TypeExt;
|
use greptime_proto::v1::column_data_type_extension::TypeExt;
|
||||||
use greptime_proto::v1::ddl_request::Expr;
|
use greptime_proto::v1::ddl_request::Expr;
|
||||||
use greptime_proto::v1::greptime_request::Request;
|
use greptime_proto::v1::greptime_request::Request;
|
||||||
use greptime_proto::v1::query_request::Query;
|
use greptime_proto::v1::query_request::Query;
|
||||||
use greptime_proto::v1::value::ValueData;
|
use greptime_proto::v1::value::ValueData;
|
||||||
use greptime_proto::v1::{
|
use greptime_proto::v1::{
|
||||||
ColumnDataTypeExtension, DdlRequest, DecimalTypeExtension, JsonTypeExtension, QueryRequest,
|
self, ColumnDataTypeExtension, DdlRequest, DecimalTypeExtension, JsonTypeExtension,
|
||||||
Row, SemanticType,
|
QueryRequest, Row, SemanticType, VectorTypeExtension,
|
||||||
};
|
};
|
||||||
use paste::paste;
|
use paste::paste;
|
||||||
use snafu::prelude::*;
|
use snafu::prelude::*;
|
||||||
@@ -150,6 +149,17 @@ impl From<ColumnDataTypeWrapper> for ConcreteDataType {
|
|||||||
ConcreteDataType::decimal128_default_datatype()
|
ConcreteDataType::decimal128_default_datatype()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ColumnDataType::Vector => {
|
||||||
|
if let Some(TypeExt::VectorType(d)) = datatype_wrapper
|
||||||
|
.datatype_ext
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|datatype_ext| datatype_ext.type_ext.as_ref())
|
||||||
|
{
|
||||||
|
ConcreteDataType::vector_datatype(d.dim)
|
||||||
|
} else {
|
||||||
|
ConcreteDataType::vector_default_datatype()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -231,6 +241,15 @@ impl ColumnDataTypeWrapper {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn vector_datatype(dim: u32) -> Self {
|
||||||
|
ColumnDataTypeWrapper {
|
||||||
|
datatype: ColumnDataType::Vector,
|
||||||
|
datatype_ext: Some(ColumnDataTypeExtension {
|
||||||
|
type_ext: Some(TypeExt::VectorType(VectorTypeExtension { dim })),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<ConcreteDataType> for ColumnDataTypeWrapper {
|
impl TryFrom<ConcreteDataType> for ColumnDataTypeWrapper {
|
||||||
@@ -249,7 +268,7 @@ impl TryFrom<ConcreteDataType> for ColumnDataTypeWrapper {
|
|||||||
ConcreteDataType::UInt64(_) => ColumnDataType::Uint64,
|
ConcreteDataType::UInt64(_) => ColumnDataType::Uint64,
|
||||||
ConcreteDataType::Float32(_) => ColumnDataType::Float32,
|
ConcreteDataType::Float32(_) => ColumnDataType::Float32,
|
||||||
ConcreteDataType::Float64(_) => ColumnDataType::Float64,
|
ConcreteDataType::Float64(_) => ColumnDataType::Float64,
|
||||||
ConcreteDataType::Binary(_) | ConcreteDataType::Json(_) => ColumnDataType::Binary,
|
ConcreteDataType::Binary(_) => ColumnDataType::Binary,
|
||||||
ConcreteDataType::String(_) => ColumnDataType::String,
|
ConcreteDataType::String(_) => ColumnDataType::String,
|
||||||
ConcreteDataType::Date(_) => ColumnDataType::Date,
|
ConcreteDataType::Date(_) => ColumnDataType::Date,
|
||||||
ConcreteDataType::DateTime(_) => ColumnDataType::Datetime,
|
ConcreteDataType::DateTime(_) => ColumnDataType::Datetime,
|
||||||
@@ -271,6 +290,8 @@ impl TryFrom<ConcreteDataType> for ColumnDataTypeWrapper {
|
|||||||
IntervalType::MonthDayNano(_) => ColumnDataType::IntervalMonthDayNano,
|
IntervalType::MonthDayNano(_) => ColumnDataType::IntervalMonthDayNano,
|
||||||
},
|
},
|
||||||
ConcreteDataType::Decimal128(_) => ColumnDataType::Decimal128,
|
ConcreteDataType::Decimal128(_) => ColumnDataType::Decimal128,
|
||||||
|
ConcreteDataType::Json(_) => ColumnDataType::Json,
|
||||||
|
ConcreteDataType::Vector(_) => ColumnDataType::Vector,
|
||||||
ConcreteDataType::Null(_)
|
ConcreteDataType::Null(_)
|
||||||
| ConcreteDataType::List(_)
|
| ConcreteDataType::List(_)
|
||||||
| ConcreteDataType::Dictionary(_)
|
| ConcreteDataType::Dictionary(_)
|
||||||
@@ -289,15 +310,17 @@ impl TryFrom<ConcreteDataType> for ColumnDataTypeWrapper {
|
|||||||
})),
|
})),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ColumnDataType::Binary => {
|
ColumnDataType::Json => datatype.as_json().map(|_| ColumnDataTypeExtension {
|
||||||
if datatype == ConcreteDataType::json_datatype() {
|
type_ext: Some(TypeExt::JsonType(JsonTypeExtension::JsonBinary.into())),
|
||||||
// Json is the same as binary in proto. The extension marks the binary in proto is actually a json.
|
}),
|
||||||
Some(ColumnDataTypeExtension {
|
ColumnDataType::Vector => {
|
||||||
type_ext: Some(TypeExt::JsonType(JsonTypeExtension::JsonBinary.into())),
|
datatype
|
||||||
|
.as_vector()
|
||||||
|
.map(|vector_type| ColumnDataTypeExtension {
|
||||||
|
type_ext: Some(TypeExt::VectorType(VectorTypeExtension {
|
||||||
|
dim: vector_type.dim as _,
|
||||||
|
})),
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
@@ -422,6 +445,10 @@ pub fn values_with_capacity(datatype: ColumnDataType, capacity: usize) -> Values
|
|||||||
string_values: Vec::with_capacity(capacity),
|
string_values: Vec::with_capacity(capacity),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
ColumnDataType::Vector => Values {
|
||||||
|
binary_values: Vec::with_capacity(capacity),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -673,6 +700,7 @@ pub fn pb_values_to_vector_ref(data_type: &ConcreteDataType, values: Values) ->
|
|||||||
Decimal128::from_value_precision_scale(x.hi, x.lo, d.precision(), d.scale()).into()
|
Decimal128::from_value_precision_scale(x.hi, x.lo, d.precision(), d.scale()).into()
|
||||||
}),
|
}),
|
||||||
)),
|
)),
|
||||||
|
ConcreteDataType::Vector(_) => Arc::new(BinaryVector::from_vec(values.binary_values)),
|
||||||
ConcreteDataType::Null(_)
|
ConcreteDataType::Null(_)
|
||||||
| ConcreteDataType::List(_)
|
| ConcreteDataType::List(_)
|
||||||
| ConcreteDataType::Dictionary(_)
|
| ConcreteDataType::Dictionary(_)
|
||||||
@@ -838,6 +866,7 @@ pub fn pb_values_to_values(data_type: &ConcreteDataType, values: Values) -> Vec<
|
|||||||
))
|
))
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
ConcreteDataType::Vector(_) => values.binary_values.into_iter().map(|v| v.into()).collect(),
|
||||||
ConcreteDataType::Null(_)
|
ConcreteDataType::Null(_)
|
||||||
| ConcreteDataType::List(_)
|
| ConcreteDataType::List(_)
|
||||||
| ConcreteDataType::Dictionary(_)
|
| ConcreteDataType::Dictionary(_)
|
||||||
@@ -862,10 +891,7 @@ pub fn is_column_type_value_eq(
|
|||||||
ColumnDataTypeWrapper::try_new(type_value, type_extension)
|
ColumnDataTypeWrapper::try_new(type_value, type_extension)
|
||||||
.map(|wrapper| {
|
.map(|wrapper| {
|
||||||
let datatype = ConcreteDataType::from(wrapper);
|
let datatype = ConcreteDataType::from(wrapper);
|
||||||
(datatype == *expect_type)
|
expect_type == &datatype
|
||||||
// Json type leverage binary type in pb, so this is valid.
|
|
||||||
|| (datatype == ConcreteDataType::binary_datatype()
|
|
||||||
&& *expect_type == ConcreteDataType::json_datatype())
|
|
||||||
})
|
})
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
@@ -1152,6 +1178,10 @@ mod tests {
|
|||||||
let values = values_with_capacity(ColumnDataType::Decimal128, 2);
|
let values = values_with_capacity(ColumnDataType::Decimal128, 2);
|
||||||
let values = values.decimal128_values;
|
let values = values.decimal128_values;
|
||||||
assert_eq!(2, values.capacity());
|
assert_eq!(2, values.capacity());
|
||||||
|
|
||||||
|
let values = values_with_capacity(ColumnDataType::Vector, 2);
|
||||||
|
let values = values.binary_values;
|
||||||
|
assert_eq!(2, values.capacity());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1239,7 +1269,11 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
ConcreteDataType::decimal128_datatype(10, 2),
|
ConcreteDataType::decimal128_datatype(10, 2),
|
||||||
ColumnDataTypeWrapper::decimal128_datatype(10, 2).into()
|
ColumnDataTypeWrapper::decimal128_datatype(10, 2).into()
|
||||||
)
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ConcreteDataType::vector_datatype(3),
|
||||||
|
ColumnDataTypeWrapper::vector_datatype(3).into()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1335,6 +1369,10 @@ mod tests {
|
|||||||
.try_into()
|
.try_into()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ColumnDataTypeWrapper::vector_datatype(3),
|
||||||
|
ConcreteDataType::vector_datatype(3).try_into().unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
let result: Result<ColumnDataTypeWrapper> = ConcreteDataType::null_datatype().try_into();
|
let result: Result<ColumnDataTypeWrapper> = ConcreteDataType::null_datatype().try_into();
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
|
|||||||
@@ -15,8 +15,10 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use datatypes::schema::{
|
use datatypes::schema::{
|
||||||
ColumnDefaultConstraint, ColumnSchema, FulltextOptions, COMMENT_KEY, FULLTEXT_KEY,
|
ColumnDefaultConstraint, ColumnSchema, FulltextAnalyzer, FulltextOptions, COMMENT_KEY,
|
||||||
|
FULLTEXT_KEY, INVERTED_INDEX_KEY,
|
||||||
};
|
};
|
||||||
|
use greptime_proto::v1::Analyzer;
|
||||||
use snafu::ResultExt;
|
use snafu::ResultExt;
|
||||||
|
|
||||||
use crate::error::{self, Result};
|
use crate::error::{self, Result};
|
||||||
@@ -25,6 +27,8 @@ use crate::v1::{ColumnDef, ColumnOptions, SemanticType};
|
|||||||
|
|
||||||
/// Key used to store fulltext options in gRPC column options.
|
/// Key used to store fulltext options in gRPC column options.
|
||||||
const FULLTEXT_GRPC_KEY: &str = "fulltext";
|
const FULLTEXT_GRPC_KEY: &str = "fulltext";
|
||||||
|
/// Key used to store inverted index options in gRPC column options.
|
||||||
|
const INVERTED_INDEX_GRPC_KEY: &str = "inverted_index";
|
||||||
|
|
||||||
/// Tries to construct a `ColumnSchema` from the given `ColumnDef`.
|
/// Tries to construct a `ColumnSchema` from the given `ColumnDef`.
|
||||||
pub fn try_as_column_schema(column_def: &ColumnDef) -> Result<ColumnSchema> {
|
pub fn try_as_column_schema(column_def: &ColumnDef) -> Result<ColumnSchema> {
|
||||||
@@ -49,10 +53,13 @@ pub fn try_as_column_schema(column_def: &ColumnDef) -> Result<ColumnSchema> {
|
|||||||
if !column_def.comment.is_empty() {
|
if !column_def.comment.is_empty() {
|
||||||
metadata.insert(COMMENT_KEY.to_string(), column_def.comment.clone());
|
metadata.insert(COMMENT_KEY.to_string(), column_def.comment.clone());
|
||||||
}
|
}
|
||||||
if let Some(options) = column_def.options.as_ref()
|
if let Some(options) = column_def.options.as_ref() {
|
||||||
&& let Some(fulltext) = options.options.get(FULLTEXT_GRPC_KEY)
|
if let Some(fulltext) = options.options.get(FULLTEXT_GRPC_KEY) {
|
||||||
{
|
metadata.insert(FULLTEXT_KEY.to_string(), fulltext.clone());
|
||||||
metadata.insert(FULLTEXT_KEY.to_string(), fulltext.to_string());
|
}
|
||||||
|
if let Some(inverted_index) = options.options.get(INVERTED_INDEX_GRPC_KEY) {
|
||||||
|
metadata.insert(INVERTED_INDEX_KEY.to_string(), inverted_index.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnSchema::new(&column_def.name, data_type.into(), column_def.is_nullable)
|
ColumnSchema::new(&column_def.name, data_type.into(), column_def.is_nullable)
|
||||||
@@ -70,7 +77,12 @@ pub fn options_from_column_schema(column_schema: &ColumnSchema) -> Option<Column
|
|||||||
if let Some(fulltext) = column_schema.metadata().get(FULLTEXT_KEY) {
|
if let Some(fulltext) = column_schema.metadata().get(FULLTEXT_KEY) {
|
||||||
options
|
options
|
||||||
.options
|
.options
|
||||||
.insert(FULLTEXT_GRPC_KEY.to_string(), fulltext.to_string());
|
.insert(FULLTEXT_GRPC_KEY.to_string(), fulltext.clone());
|
||||||
|
}
|
||||||
|
if let Some(inverted_index) = column_schema.metadata().get(INVERTED_INDEX_KEY) {
|
||||||
|
options
|
||||||
|
.options
|
||||||
|
.insert(INVERTED_INDEX_GRPC_KEY.to_string(), inverted_index.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
(!options.options.is_empty()).then_some(options)
|
(!options.options.is_empty()).then_some(options)
|
||||||
@@ -93,6 +105,14 @@ pub fn options_from_fulltext(fulltext: &FulltextOptions) -> Result<Option<Column
|
|||||||
Ok((!options.options.is_empty()).then_some(options))
|
Ok((!options.options.is_empty()).then_some(options))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tries to construct a `FulltextAnalyzer` from the given analyzer.
|
||||||
|
pub fn as_fulltext_option(analyzer: Analyzer) -> FulltextAnalyzer {
|
||||||
|
match analyzer {
|
||||||
|
Analyzer::English => FulltextAnalyzer::English,
|
||||||
|
Analyzer::Chinese => FulltextAnalyzer::Chinese,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
@@ -115,10 +135,13 @@ mod tests {
|
|||||||
comment: "test_comment".to_string(),
|
comment: "test_comment".to_string(),
|
||||||
datatype_extension: None,
|
datatype_extension: None,
|
||||||
options: Some(ColumnOptions {
|
options: Some(ColumnOptions {
|
||||||
options: HashMap::from([(
|
options: HashMap::from([
|
||||||
FULLTEXT_GRPC_KEY.to_string(),
|
(
|
||||||
"{\"enable\":true}".to_string(),
|
FULLTEXT_GRPC_KEY.to_string(),
|
||||||
)]),
|
"{\"enable\":true}".to_string(),
|
||||||
|
),
|
||||||
|
(INVERTED_INDEX_GRPC_KEY.to_string(), "true".to_string()),
|
||||||
|
]),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -139,6 +162,7 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
assert!(schema.is_inverted_indexed());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -153,12 +177,17 @@ mod tests {
|
|||||||
analyzer: FulltextAnalyzer::English,
|
analyzer: FulltextAnalyzer::English,
|
||||||
case_sensitive: false,
|
case_sensitive: false,
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap()
|
||||||
|
.set_inverted_index(true);
|
||||||
let options = options_from_column_schema(&schema).unwrap();
|
let options = options_from_column_schema(&schema).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
options.options.get(FULLTEXT_GRPC_KEY).unwrap(),
|
options.options.get(FULLTEXT_GRPC_KEY).unwrap(),
|
||||||
"{\"enable\":true,\"analyzer\":\"English\",\"case-sensitive\":false}"
|
"{\"enable\":true,\"analyzer\":\"English\",\"case-sensitive\":false}"
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
options.options.get(INVERTED_INDEX_GRPC_KEY).unwrap(),
|
||||||
|
"true"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -178,6 +178,12 @@ pub enum Error {
|
|||||||
location: Location,
|
location: Location,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Partition manager not found, it's not expected."))]
|
||||||
|
PartitionManagerNotFound {
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
},
|
||||||
|
|
||||||
#[snafu(display("Failed to find table partitions"))]
|
#[snafu(display("Failed to find table partitions"))]
|
||||||
FindPartitions { source: partition::error::Error },
|
FindPartitions { source: partition::error::Error },
|
||||||
|
|
||||||
@@ -301,6 +307,7 @@ impl ErrorExt for Error {
|
|||||||
| Error::CastManager { .. }
|
| Error::CastManager { .. }
|
||||||
| Error::Json { .. }
|
| Error::Json { .. }
|
||||||
| Error::GetInformationExtension { .. }
|
| Error::GetInformationExtension { .. }
|
||||||
|
| Error::PartitionManagerNotFound { .. }
|
||||||
| Error::ProcedureIdNotFound { .. } => StatusCode::Unexpected,
|
| Error::ProcedureIdNotFound { .. } => StatusCode::Unexpected,
|
||||||
|
|
||||||
Error::ViewPlanColumnsChanged { .. } => StatusCode::InvalidArguments,
|
Error::ViewPlanColumnsChanged { .. } => StatusCode::InvalidArguments,
|
||||||
|
|||||||
@@ -34,15 +34,14 @@ use datatypes::vectors::{
|
|||||||
};
|
};
|
||||||
use futures::{StreamExt, TryStreamExt};
|
use futures::{StreamExt, TryStreamExt};
|
||||||
use partition::manager::PartitionInfo;
|
use partition::manager::PartitionInfo;
|
||||||
use partition::partition::PartitionDef;
|
|
||||||
use snafu::{OptionExt, ResultExt};
|
use snafu::{OptionExt, ResultExt};
|
||||||
use store_api::storage::{RegionId, ScanRequest, TableId};
|
use store_api::storage::{ScanRequest, TableId};
|
||||||
use table::metadata::{TableInfo, TableType};
|
use table::metadata::{TableInfo, TableType};
|
||||||
|
|
||||||
use super::PARTITIONS;
|
use super::PARTITIONS;
|
||||||
use crate::error::{
|
use crate::error::{
|
||||||
CreateRecordBatchSnafu, FindPartitionsSnafu, InternalSnafu, Result,
|
CreateRecordBatchSnafu, FindPartitionsSnafu, InternalSnafu, PartitionManagerNotFoundSnafu,
|
||||||
UpgradeWeakCatalogManagerRefSnafu,
|
Result, UpgradeWeakCatalogManagerRefSnafu,
|
||||||
};
|
};
|
||||||
use crate::kvbackend::KvBackendCatalogManager;
|
use crate::kvbackend::KvBackendCatalogManager;
|
||||||
use crate::system_schema::information_schema::{InformationTable, Predicates};
|
use crate::system_schema::information_schema::{InformationTable, Predicates};
|
||||||
@@ -236,7 +235,8 @@ impl InformationSchemaPartitionsBuilder {
|
|||||||
let partition_manager = catalog_manager
|
let partition_manager = catalog_manager
|
||||||
.as_any()
|
.as_any()
|
||||||
.downcast_ref::<KvBackendCatalogManager>()
|
.downcast_ref::<KvBackendCatalogManager>()
|
||||||
.map(|catalog_manager| catalog_manager.partition_manager());
|
.map(|catalog_manager| catalog_manager.partition_manager())
|
||||||
|
.context(PartitionManagerNotFoundSnafu)?;
|
||||||
|
|
||||||
let predicates = Predicates::from_scan_request(&request);
|
let predicates = Predicates::from_scan_request(&request);
|
||||||
|
|
||||||
@@ -262,27 +262,10 @@ impl InformationSchemaPartitionsBuilder {
|
|||||||
let table_ids: Vec<TableId> =
|
let table_ids: Vec<TableId> =
|
||||||
table_infos.iter().map(|info| info.ident.table_id).collect();
|
table_infos.iter().map(|info| info.ident.table_id).collect();
|
||||||
|
|
||||||
let mut table_partitions = if let Some(partition_manager) = &partition_manager {
|
let mut table_partitions = partition_manager
|
||||||
partition_manager
|
.batch_find_table_partitions(&table_ids)
|
||||||
.batch_find_table_partitions(&table_ids)
|
.await
|
||||||
.await
|
.context(FindPartitionsSnafu)?;
|
||||||
.context(FindPartitionsSnafu)?
|
|
||||||
} else {
|
|
||||||
// Current node must be a standalone instance, contains only one partition by default.
|
|
||||||
// TODO(dennis): change it when we support multi-regions for standalone.
|
|
||||||
table_ids
|
|
||||||
.into_iter()
|
|
||||||
.map(|table_id| {
|
|
||||||
(
|
|
||||||
table_id,
|
|
||||||
vec![PartitionInfo {
|
|
||||||
id: RegionId::new(table_id, 0),
|
|
||||||
partition: PartitionDef::new(vec![], vec![]),
|
|
||||||
}],
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
};
|
|
||||||
|
|
||||||
for table_info in table_infos {
|
for table_info in table_infos {
|
||||||
let partitions = table_partitions
|
let partitions = table_partitions
|
||||||
|
|||||||
@@ -12,13 +12,16 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
use arrow_schema::SchemaRef as ArrowSchemaRef;
|
use arrow_schema::SchemaRef as ArrowSchemaRef;
|
||||||
use common_catalog::consts::INFORMATION_SCHEMA_TABLES_TABLE_ID;
|
use common_catalog::consts::{INFORMATION_SCHEMA_TABLES_TABLE_ID, MITO_ENGINE};
|
||||||
use common_error::ext::BoxedError;
|
use common_error::ext::BoxedError;
|
||||||
|
use common_meta::datanode::RegionStat;
|
||||||
use common_recordbatch::adapter::RecordBatchStreamAdapter;
|
use common_recordbatch::adapter::RecordBatchStreamAdapter;
|
||||||
use common_recordbatch::{RecordBatch, SendableRecordBatchStream};
|
use common_recordbatch::{RecordBatch, SendableRecordBatchStream};
|
||||||
|
use common_telemetry::error;
|
||||||
use datafusion::execution::TaskContext;
|
use datafusion::execution::TaskContext;
|
||||||
use datafusion::physical_plan::stream::RecordBatchStreamAdapter as DfRecordBatchStreamAdapter;
|
use datafusion::physical_plan::stream::RecordBatchStreamAdapter as DfRecordBatchStreamAdapter;
|
||||||
use datafusion::physical_plan::streaming::PartitionStream as DfPartitionStream;
|
use datafusion::physical_plan::streaming::PartitionStream as DfPartitionStream;
|
||||||
@@ -31,7 +34,7 @@ use datatypes::vectors::{
|
|||||||
};
|
};
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
use snafu::{OptionExt, ResultExt};
|
use snafu::{OptionExt, ResultExt};
|
||||||
use store_api::storage::{ScanRequest, TableId};
|
use store_api::storage::{RegionId, ScanRequest, TableId};
|
||||||
use table::metadata::{TableInfo, TableType};
|
use table::metadata::{TableInfo, TableType};
|
||||||
|
|
||||||
use super::TABLES;
|
use super::TABLES;
|
||||||
@@ -39,6 +42,7 @@ use crate::error::{
|
|||||||
CreateRecordBatchSnafu, InternalSnafu, Result, UpgradeWeakCatalogManagerRefSnafu,
|
CreateRecordBatchSnafu, InternalSnafu, Result, UpgradeWeakCatalogManagerRefSnafu,
|
||||||
};
|
};
|
||||||
use crate::system_schema::information_schema::{InformationTable, Predicates};
|
use crate::system_schema::information_schema::{InformationTable, Predicates};
|
||||||
|
use crate::system_schema::utils;
|
||||||
use crate::CatalogManager;
|
use crate::CatalogManager;
|
||||||
|
|
||||||
pub const TABLE_CATALOG: &str = "table_catalog";
|
pub const TABLE_CATALOG: &str = "table_catalog";
|
||||||
@@ -234,17 +238,51 @@ impl InformationSchemaTablesBuilder {
|
|||||||
.context(UpgradeWeakCatalogManagerRefSnafu)?;
|
.context(UpgradeWeakCatalogManagerRefSnafu)?;
|
||||||
let predicates = Predicates::from_scan_request(&request);
|
let predicates = Predicates::from_scan_request(&request);
|
||||||
|
|
||||||
|
let information_extension = utils::information_extension(&self.catalog_manager)?;
|
||||||
|
|
||||||
|
// TODO(dennis): `region_stats` API is not stable in distributed cluster because of network issue etc.
|
||||||
|
// But we don't want the statements such as `show tables` fail,
|
||||||
|
// so using `unwrap_or_else` here instead of `?` operator.
|
||||||
|
let region_stats = information_extension
|
||||||
|
.region_stats()
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
error!(e; "Failed to call region_stats");
|
||||||
|
e
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|_| vec![]);
|
||||||
|
|
||||||
for schema_name in catalog_manager.schema_names(&catalog_name, None).await? {
|
for schema_name in catalog_manager.schema_names(&catalog_name, None).await? {
|
||||||
let mut stream = catalog_manager.tables(&catalog_name, &schema_name, None);
|
let mut stream = catalog_manager.tables(&catalog_name, &schema_name, None);
|
||||||
|
|
||||||
while let Some(table) = stream.try_next().await? {
|
while let Some(table) = stream.try_next().await? {
|
||||||
let table_info = table.table_info();
|
let table_info = table.table_info();
|
||||||
|
|
||||||
|
// TODO(dennis): make it working for metric engine
|
||||||
|
let table_region_stats =
|
||||||
|
if table_info.meta.engine == MITO_ENGINE || table_info.is_physical_table() {
|
||||||
|
let region_ids = table_info
|
||||||
|
.meta
|
||||||
|
.region_numbers
|
||||||
|
.iter()
|
||||||
|
.map(|n| RegionId::new(table_info.ident.table_id, *n))
|
||||||
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
|
region_stats
|
||||||
|
.iter()
|
||||||
|
.filter(|stat| region_ids.contains(&stat.id))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
self.add_table(
|
self.add_table(
|
||||||
&predicates,
|
&predicates,
|
||||||
&catalog_name,
|
&catalog_name,
|
||||||
&schema_name,
|
&schema_name,
|
||||||
table_info,
|
table_info,
|
||||||
table.table_type(),
|
table.table_type(),
|
||||||
|
&table_region_stats,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,6 +298,7 @@ impl InformationSchemaTablesBuilder {
|
|||||||
schema_name: &str,
|
schema_name: &str,
|
||||||
table_info: Arc<TableInfo>,
|
table_info: Arc<TableInfo>,
|
||||||
table_type: TableType,
|
table_type: TableType,
|
||||||
|
region_stats: &[&RegionStat],
|
||||||
) {
|
) {
|
||||||
let table_name = table_info.name.as_ref();
|
let table_name = table_info.name.as_ref();
|
||||||
let table_id = table_info.table_id();
|
let table_id = table_info.table_id();
|
||||||
@@ -273,7 +312,9 @@ impl InformationSchemaTablesBuilder {
|
|||||||
|
|
||||||
let row = [
|
let row = [
|
||||||
(TABLE_CATALOG, &Value::from(catalog_name)),
|
(TABLE_CATALOG, &Value::from(catalog_name)),
|
||||||
|
(TABLE_ID, &Value::from(table_id)),
|
||||||
(TABLE_SCHEMA, &Value::from(schema_name)),
|
(TABLE_SCHEMA, &Value::from(schema_name)),
|
||||||
|
(ENGINE, &Value::from(engine)),
|
||||||
(TABLE_NAME, &Value::from(table_name)),
|
(TABLE_NAME, &Value::from(table_name)),
|
||||||
(TABLE_TYPE, &Value::from(table_type_text)),
|
(TABLE_TYPE, &Value::from(table_type_text)),
|
||||||
];
|
];
|
||||||
@@ -287,21 +328,39 @@ impl InformationSchemaTablesBuilder {
|
|||||||
self.table_names.push(Some(table_name));
|
self.table_names.push(Some(table_name));
|
||||||
self.table_types.push(Some(table_type_text));
|
self.table_types.push(Some(table_type_text));
|
||||||
self.table_ids.push(Some(table_id));
|
self.table_ids.push(Some(table_id));
|
||||||
|
|
||||||
|
let data_length = region_stats.iter().map(|stat| stat.sst_size).sum();
|
||||||
|
let table_rows = region_stats.iter().map(|stat| stat.num_rows).sum();
|
||||||
|
let index_length = region_stats.iter().map(|stat| stat.index_size).sum();
|
||||||
|
|
||||||
|
// It's not precise, but it is acceptable for long-term data storage.
|
||||||
|
let avg_row_length = if table_rows > 0 {
|
||||||
|
let total_data_length = data_length
|
||||||
|
+ region_stats
|
||||||
|
.iter()
|
||||||
|
.map(|stat| stat.memtable_size)
|
||||||
|
.sum::<u64>();
|
||||||
|
|
||||||
|
total_data_length / table_rows
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
self.data_length.push(Some(data_length));
|
||||||
|
self.index_length.push(Some(index_length));
|
||||||
|
self.table_rows.push(Some(table_rows));
|
||||||
|
self.avg_row_length.push(Some(avg_row_length));
|
||||||
|
|
||||||
// TODO(sunng87): use real data for these fields
|
// TODO(sunng87): use real data for these fields
|
||||||
self.data_length.push(Some(0));
|
|
||||||
self.max_data_length.push(Some(0));
|
self.max_data_length.push(Some(0));
|
||||||
self.index_length.push(Some(0));
|
|
||||||
self.avg_row_length.push(Some(0));
|
|
||||||
self.max_index_length.push(Some(0));
|
|
||||||
self.checksum.push(Some(0));
|
self.checksum.push(Some(0));
|
||||||
self.table_rows.push(Some(0));
|
self.max_index_length.push(Some(0));
|
||||||
self.data_free.push(Some(0));
|
self.data_free.push(Some(0));
|
||||||
self.auto_increment.push(Some(0));
|
self.auto_increment.push(Some(0));
|
||||||
self.row_format.push(Some("Fixed"));
|
self.row_format.push(Some("Fixed"));
|
||||||
self.table_collation.push(Some("utf8_bin"));
|
self.table_collation.push(Some("utf8_bin"));
|
||||||
self.update_time.push(None);
|
self.update_time.push(None);
|
||||||
self.check_time.push(None);
|
self.check_time.push(None);
|
||||||
|
|
||||||
// use mariadb default table version number here
|
// use mariadb default table version number here
|
||||||
self.version.push(Some(11));
|
self.version.push(Some(11));
|
||||||
self.table_comment.push(table_info.desc.as_deref());
|
self.table_comment.push(table_info.desc.as_deref());
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ common-error.workspace = true
|
|||||||
common-grpc.workspace = true
|
common-grpc.workspace = true
|
||||||
common-macro.workspace = true
|
common-macro.workspace = true
|
||||||
common-meta.workspace = true
|
common-meta.workspace = true
|
||||||
|
common-options.workspace = true
|
||||||
common-procedure.workspace = true
|
common-procedure.workspace = true
|
||||||
common-query.workspace = true
|
common-query.workspace = true
|
||||||
common-recordbatch.workspace = true
|
common-recordbatch.workspace = true
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ use common_config::Configurable;
|
|||||||
use common_grpc::channel_manager::{
|
use common_grpc::channel_manager::{
|
||||||
DEFAULT_MAX_GRPC_RECV_MESSAGE_SIZE, DEFAULT_MAX_GRPC_SEND_MESSAGE_SIZE,
|
DEFAULT_MAX_GRPC_RECV_MESSAGE_SIZE, DEFAULT_MAX_GRPC_SEND_MESSAGE_SIZE,
|
||||||
};
|
};
|
||||||
|
use common_options::datanode::{ClientOptions, DatanodeClientOptions};
|
||||||
use common_telemetry::logging::{LoggingOptions, SlowQueryOptions, DEFAULT_OTLP_ENDPOINT};
|
use common_telemetry::logging::{LoggingOptions, SlowQueryOptions, DEFAULT_OTLP_ENDPOINT};
|
||||||
use common_wal::config::raft_engine::RaftEngineConfig;
|
use common_wal::config::raft_engine::RaftEngineConfig;
|
||||||
use common_wal::config::DatanodeWalConfig;
|
use common_wal::config::DatanodeWalConfig;
|
||||||
use datanode::config::{DatanodeOptions, RegionEngineConfig, StorageConfig};
|
use datanode::config::{DatanodeOptions, RegionEngineConfig, StorageConfig};
|
||||||
use file_engine::config::EngineConfig;
|
use file_engine::config::EngineConfig;
|
||||||
use frontend::frontend::FrontendOptions;
|
use frontend::frontend::FrontendOptions;
|
||||||
use frontend::service_config::datanode::DatanodeClientOptions;
|
|
||||||
use meta_client::MetaClientOptions;
|
use meta_client::MetaClientOptions;
|
||||||
use meta_srv::metasrv::MetasrvOptions;
|
use meta_srv::metasrv::MetasrvOptions;
|
||||||
use meta_srv::selector::SelectorType;
|
use meta_srv::selector::SelectorType;
|
||||||
@@ -126,10 +126,11 @@ fn test_load_frontend_example_config() {
|
|||||||
tracing_sample_ratio: Some(Default::default()),
|
tracing_sample_ratio: Some(Default::default()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
datanode: frontend::service_config::DatanodeOptions {
|
datanode: DatanodeClientOptions {
|
||||||
client: DatanodeClientOptions {
|
client: ClientOptions {
|
||||||
connect_timeout: Duration::from_secs(10),
|
connect_timeout: Duration::from_secs(10),
|
||||||
tcp_nodelay: true,
|
tcp_nodelay: true,
|
||||||
|
..Default::default()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
export_metrics: ExportMetricsOption {
|
export_metrics: ExportMetricsOption {
|
||||||
@@ -166,8 +167,8 @@ fn test_load_metasrv_example_config() {
|
|||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
datanode: meta_srv::metasrv::DatanodeOptions {
|
datanode: DatanodeClientOptions {
|
||||||
client: meta_srv::metasrv::DatanodeClientOptions {
|
client: ClientOptions {
|
||||||
timeout: Duration::from_secs(10),
|
timeout: Duration::from_secs(10),
|
||||||
connect_timeout: Duration::from_secs(10),
|
connect_timeout: Duration::from_secs(10),
|
||||||
tcp_nodelay: true,
|
tcp_nodelay: true,
|
||||||
|
|||||||
@@ -16,9 +16,12 @@ common-error.workspace = true
|
|||||||
common-macro.workspace = true
|
common-macro.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
paste = "1.0"
|
paste = "1.0"
|
||||||
|
pin-project.workspace = true
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
snafu.workspace = true
|
snafu.workspace = true
|
||||||
|
tokio.workspace = true
|
||||||
zeroize = { version = "1.6", default-features = false, features = ["alloc"] }
|
zeroize = { version = "1.6", default-features = false, features = ["alloc"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
common-test-util.workspace = true
|
||||||
toml.workspace = true
|
toml.workspace = true
|
||||||
|
|||||||
@@ -12,12 +12,20 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use std::future::Future;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use bytes::{BufMut, Bytes};
|
use bytes::{BufMut, Bytes};
|
||||||
use futures::{AsyncReadExt, AsyncSeekExt};
|
use futures::AsyncRead;
|
||||||
|
use pin_project::pin_project;
|
||||||
|
use tokio::io::{AsyncReadExt as _, AsyncSeekExt as _};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
/// `Metadata` contains the metadata of a source.
|
/// `Metadata` contains the metadata of a source.
|
||||||
pub struct Metadata {
|
pub struct Metadata {
|
||||||
@@ -61,7 +69,7 @@ pub trait RangeReader: Send + Unpin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<R: RangeReader + Send + Unpin> RangeReader for &mut R {
|
impl<R: ?Sized + RangeReader> RangeReader for &mut R {
|
||||||
async fn metadata(&mut self) -> io::Result<Metadata> {
|
async fn metadata(&mut self) -> io::Result<Metadata> {
|
||||||
(*self).metadata().await
|
(*self).metadata().await
|
||||||
}
|
}
|
||||||
@@ -80,26 +88,212 @@ impl<R: RangeReader + Send + Unpin> RangeReader for &mut R {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `RangeReaderAdapter` bridges `RangeReader` and `AsyncRead + AsyncSeek`.
|
/// `AsyncReadAdapter` adapts a `RangeReader` to an `AsyncRead`.
|
||||||
pub struct RangeReaderAdapter<R>(pub R);
|
#[pin_project]
|
||||||
|
pub struct AsyncReadAdapter<R> {
|
||||||
|
/// The inner `RangeReader`.
|
||||||
|
/// Use `Mutex` to get rid of the borrow checker issue.
|
||||||
|
inner: Arc<Mutex<R>>,
|
||||||
|
|
||||||
|
/// The current position from the view of the reader.
|
||||||
|
position: u64,
|
||||||
|
|
||||||
|
/// The buffer for the read bytes.
|
||||||
|
buffer: Vec<u8>,
|
||||||
|
|
||||||
|
/// The length of the content.
|
||||||
|
content_length: u64,
|
||||||
|
|
||||||
|
/// The future for reading the next bytes.
|
||||||
|
#[pin]
|
||||||
|
read_fut: Option<Pin<Box<dyn Future<Output = io::Result<Bytes>> + Send>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: RangeReader + 'static> AsyncReadAdapter<R> {
|
||||||
|
pub async fn new(inner: R) -> io::Result<Self> {
|
||||||
|
let mut inner = inner;
|
||||||
|
let metadata = inner.metadata().await?;
|
||||||
|
Ok(AsyncReadAdapter {
|
||||||
|
inner: Arc::new(Mutex::new(inner)),
|
||||||
|
position: 0,
|
||||||
|
buffer: Vec::new(),
|
||||||
|
content_length: metadata.content_length,
|
||||||
|
read_fut: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The maximum size per read for the inner reader in `AsyncReadAdapter`.
|
||||||
|
const MAX_SIZE_PER_READ: usize = 8 * 1024 * 1024; // 8MB
|
||||||
|
|
||||||
|
impl<R: RangeReader + 'static> AsyncRead for AsyncReadAdapter<R> {
|
||||||
|
fn poll_read(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &mut [u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
let mut this = self.as_mut().project();
|
||||||
|
|
||||||
|
if *this.position >= *this.content_length {
|
||||||
|
return Poll::Ready(Ok(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !this.buffer.is_empty() {
|
||||||
|
let to_read = this.buffer.len().min(buf.len());
|
||||||
|
buf[..to_read].copy_from_slice(&this.buffer[..to_read]);
|
||||||
|
this.buffer.drain(..to_read);
|
||||||
|
*this.position += to_read as u64;
|
||||||
|
return Poll::Ready(Ok(to_read));
|
||||||
|
}
|
||||||
|
|
||||||
|
if this.read_fut.is_none() {
|
||||||
|
let size = (*this.content_length - *this.position).min(MAX_SIZE_PER_READ as u64);
|
||||||
|
let range = *this.position..(*this.position + size);
|
||||||
|
let inner = this.inner.clone();
|
||||||
|
let fut = async move {
|
||||||
|
let mut inner = inner.lock().await;
|
||||||
|
inner.read(range).await
|
||||||
|
};
|
||||||
|
|
||||||
|
*this.read_fut = Some(Box::pin(fut));
|
||||||
|
}
|
||||||
|
|
||||||
|
match this
|
||||||
|
.read_fut
|
||||||
|
.as_mut()
|
||||||
|
.as_pin_mut()
|
||||||
|
.expect("checked above")
|
||||||
|
.poll(cx)
|
||||||
|
{
|
||||||
|
Poll::Pending => Poll::Pending,
|
||||||
|
Poll::Ready(Ok(bytes)) => {
|
||||||
|
*this.read_fut = None;
|
||||||
|
|
||||||
|
if !bytes.is_empty() {
|
||||||
|
this.buffer.extend_from_slice(&bytes);
|
||||||
|
self.poll_read(cx, buf)
|
||||||
|
} else {
|
||||||
|
Poll::Ready(Ok(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Poll::Ready(Err(e)) => {
|
||||||
|
*this.read_fut = None;
|
||||||
|
Poll::Ready(Err(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Implements `RangeReader` for a type that implements `AsyncRead + AsyncSeek`.
|
|
||||||
///
|
|
||||||
/// TODO(zhongzc): It's a temporary solution for porting the codebase from `AsyncRead + AsyncSeek` to `RangeReader`.
|
|
||||||
/// Until the codebase is fully ported to `RangeReader`, remove this implementation.
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<R: futures::AsyncRead + futures::AsyncSeek + Send + Unpin> RangeReader
|
impl RangeReader for Vec<u8> {
|
||||||
for RangeReaderAdapter<R>
|
|
||||||
{
|
|
||||||
async fn metadata(&mut self) -> io::Result<Metadata> {
|
async fn metadata(&mut self) -> io::Result<Metadata> {
|
||||||
let content_length = self.0.seek(io::SeekFrom::End(0)).await?;
|
Ok(Metadata {
|
||||||
Ok(Metadata { content_length })
|
content_length: self.len() as u64,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read(&mut self, range: Range<u64>) -> io::Result<Bytes> {
|
async fn read(&mut self, mut range: Range<u64>) -> io::Result<Bytes> {
|
||||||
|
range.end = range.end.min(self.len() as u64);
|
||||||
|
|
||||||
|
let bytes = Bytes::copy_from_slice(&self[range.start as usize..range.end as usize]);
|
||||||
|
Ok(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `FileReader` is a `RangeReader` for reading a file.
|
||||||
|
pub struct FileReader {
|
||||||
|
content_length: u64,
|
||||||
|
position: u64,
|
||||||
|
file: tokio::fs::File,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileReader {
|
||||||
|
/// Creates a new `FileReader` for the file at the given path.
|
||||||
|
pub async fn new(path: impl AsRef<Path>) -> io::Result<Self> {
|
||||||
|
let file = tokio::fs::File::open(path).await?;
|
||||||
|
let metadata = file.metadata().await?;
|
||||||
|
Ok(FileReader {
|
||||||
|
content_length: metadata.len(),
|
||||||
|
position: 0,
|
||||||
|
file,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl RangeReader for FileReader {
|
||||||
|
async fn metadata(&mut self) -> io::Result<Metadata> {
|
||||||
|
Ok(Metadata {
|
||||||
|
content_length: self.content_length,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read(&mut self, mut range: Range<u64>) -> io::Result<Bytes> {
|
||||||
|
if range.start != self.position {
|
||||||
|
self.file.seek(io::SeekFrom::Start(range.start)).await?;
|
||||||
|
self.position = range.start;
|
||||||
|
}
|
||||||
|
|
||||||
|
range.end = range.end.min(self.content_length);
|
||||||
|
if range.end <= self.position {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::UnexpectedEof,
|
||||||
|
"Start of range is out of bounds",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let mut buf = vec![0; (range.end - range.start) as usize];
|
let mut buf = vec![0; (range.end - range.start) as usize];
|
||||||
self.0.seek(io::SeekFrom::Start(range.start)).await?;
|
|
||||||
self.0.read_exact(&mut buf).await?;
|
self.file.read_exact(&mut buf).await?;
|
||||||
|
self.position = range.end;
|
||||||
|
|
||||||
Ok(Bytes::from(buf))
|
Ok(Bytes::from(buf))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use common_test_util::temp_dir::create_named_temp_file;
|
||||||
|
use futures::io::AsyncReadExt as _;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_async_read_adapter() {
|
||||||
|
let data = b"hello world";
|
||||||
|
let reader = Vec::from(data);
|
||||||
|
let mut adapter = AsyncReadAdapter::new(reader).await.unwrap();
|
||||||
|
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
adapter.read_to_end(&mut buf).await.unwrap();
|
||||||
|
assert_eq!(buf, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_async_read_adapter_large() {
|
||||||
|
let data = (0..20 * 1024 * 1024).map(|i| i as u8).collect::<Vec<u8>>();
|
||||||
|
let mut adapter = AsyncReadAdapter::new(data.clone()).await.unwrap();
|
||||||
|
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
adapter.read_to_end(&mut buf).await.unwrap();
|
||||||
|
assert_eq!(buf, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_file_reader() {
|
||||||
|
let file = create_named_temp_file();
|
||||||
|
let path = file.path();
|
||||||
|
let data = b"hello world";
|
||||||
|
tokio::fs::write(path, data).await.unwrap();
|
||||||
|
|
||||||
|
let mut reader = FileReader::new(path).await.unwrap();
|
||||||
|
let metadata = reader.metadata().await.unwrap();
|
||||||
|
assert_eq!(metadata.content_length, data.len() as u64);
|
||||||
|
|
||||||
|
let bytes = reader.read(0..metadata.content_length).await.unwrap();
|
||||||
|
assert_eq!(&*bytes, data);
|
||||||
|
|
||||||
|
let bytes = reader.read(0..5).await.unwrap();
|
||||||
|
assert_eq!(&*bytes, &data[..5]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ workspace = true
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["geo"]
|
default = ["geo"]
|
||||||
geo = ["geohash", "h3o", "s2"]
|
geo = ["geohash", "h3o", "s2", "wkt", "geo-types", "dep:geo"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
api.workspace = true
|
api.workspace = true
|
||||||
@@ -28,6 +28,8 @@ common-version.workspace = true
|
|||||||
datafusion.workspace = true
|
datafusion.workspace = true
|
||||||
datatypes.workspace = true
|
datatypes.workspace = true
|
||||||
derive_more = { version = "1", default-features = false, features = ["display"] }
|
derive_more = { version = "1", default-features = false, features = ["display"] }
|
||||||
|
geo = { version = "0.29", optional = true }
|
||||||
|
geo-types = { version = "0.7", optional = true }
|
||||||
geohash = { version = "0.13", optional = true }
|
geohash = { version = "0.13", optional = true }
|
||||||
h3o = { version = "0.6", optional = true }
|
h3o = { version = "0.6", optional = true }
|
||||||
jsonb.workspace = true
|
jsonb.workspace = true
|
||||||
@@ -44,6 +46,7 @@ sql.workspace = true
|
|||||||
statrs = "0.16"
|
statrs = "0.16"
|
||||||
store-api.workspace = true
|
store-api.workspace = true
|
||||||
table.workspace = true
|
table.workspace = true
|
||||||
|
wkt = { version = "0.11", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ron = "0.7"
|
ron = "0.7"
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ pub(crate) mod encoding;
|
|||||||
mod geohash;
|
mod geohash;
|
||||||
mod h3;
|
mod h3;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
|
mod measure;
|
||||||
|
mod relation;
|
||||||
mod s2;
|
mod s2;
|
||||||
|
mod wkt;
|
||||||
|
|
||||||
use crate::function_registry::FunctionRegistry;
|
use crate::function_registry::FunctionRegistry;
|
||||||
|
|
||||||
@@ -48,6 +51,7 @@ impl GeoFunctions {
|
|||||||
registry.register(Arc::new(h3::H3CellToChildrenSize));
|
registry.register(Arc::new(h3::H3CellToChildrenSize));
|
||||||
registry.register(Arc::new(h3::H3CellToChildPos));
|
registry.register(Arc::new(h3::H3CellToChildPos));
|
||||||
registry.register(Arc::new(h3::H3ChildPosToCell));
|
registry.register(Arc::new(h3::H3ChildPosToCell));
|
||||||
|
registry.register(Arc::new(h3::H3CellContains));
|
||||||
|
|
||||||
// h3 grid traversal
|
// h3 grid traversal
|
||||||
registry.register(Arc::new(h3::H3GridDisk));
|
registry.register(Arc::new(h3::H3GridDisk));
|
||||||
@@ -55,10 +59,27 @@ impl GeoFunctions {
|
|||||||
registry.register(Arc::new(h3::H3GridDistance));
|
registry.register(Arc::new(h3::H3GridDistance));
|
||||||
registry.register(Arc::new(h3::H3GridPathCells));
|
registry.register(Arc::new(h3::H3GridPathCells));
|
||||||
|
|
||||||
|
// h3 measurement
|
||||||
|
registry.register(Arc::new(h3::H3CellDistanceSphereKm));
|
||||||
|
registry.register(Arc::new(h3::H3CellDistanceEuclideanDegree));
|
||||||
|
|
||||||
// s2
|
// s2
|
||||||
registry.register(Arc::new(s2::S2LatLngToCell));
|
registry.register(Arc::new(s2::S2LatLngToCell));
|
||||||
registry.register(Arc::new(s2::S2CellLevel));
|
registry.register(Arc::new(s2::S2CellLevel));
|
||||||
registry.register(Arc::new(s2::S2CellToToken));
|
registry.register(Arc::new(s2::S2CellToToken));
|
||||||
registry.register(Arc::new(s2::S2CellParent));
|
registry.register(Arc::new(s2::S2CellParent));
|
||||||
|
|
||||||
|
// spatial data type
|
||||||
|
registry.register(Arc::new(wkt::LatLngToPointWkt));
|
||||||
|
|
||||||
|
// spatial relation
|
||||||
|
registry.register(Arc::new(relation::STContains));
|
||||||
|
registry.register(Arc::new(relation::STWithin));
|
||||||
|
registry.register(Arc::new(relation::STIntersects));
|
||||||
|
|
||||||
|
// spatial measure
|
||||||
|
registry.register(Arc::new(measure::STDistance));
|
||||||
|
registry.register(Arc::new(measure::STDistanceSphere));
|
||||||
|
registry.register(Arc::new(measure::STArea));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ use datatypes::prelude::ConcreteDataType;
|
|||||||
use datatypes::scalars::{Scalar, ScalarVectorBuilder};
|
use datatypes::scalars::{Scalar, ScalarVectorBuilder};
|
||||||
use datatypes::value::{ListValue, Value};
|
use datatypes::value::{ListValue, Value};
|
||||||
use datatypes::vectors::{
|
use datatypes::vectors::{
|
||||||
BooleanVectorBuilder, Int32VectorBuilder, ListVectorBuilder, MutableVector,
|
BooleanVectorBuilder, Float64VectorBuilder, Int32VectorBuilder, ListVectorBuilder,
|
||||||
StringVectorBuilder, UInt64VectorBuilder, UInt8VectorBuilder, VectorRef,
|
MutableVector, StringVectorBuilder, UInt64VectorBuilder, UInt8VectorBuilder, VectorRef,
|
||||||
};
|
};
|
||||||
use derive_more::Display;
|
use derive_more::Display;
|
||||||
use h3o::{CellIndex, LatLng, Resolution};
|
use h3o::{CellIndex, LatLng, Resolution};
|
||||||
@@ -38,6 +38,7 @@ static CELL_TYPES: Lazy<Vec<ConcreteDataType>> = Lazy::new(|| {
|
|||||||
vec![
|
vec![
|
||||||
ConcreteDataType::int64_datatype(),
|
ConcreteDataType::int64_datatype(),
|
||||||
ConcreteDataType::uint64_datatype(),
|
ConcreteDataType::uint64_datatype(),
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -952,6 +953,181 @@ impl Function for H3GridPathCells {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tests if cells contains given cells
|
||||||
|
#[derive(Clone, Debug, Default, Display)]
|
||||||
|
#[display("{}", self.name())]
|
||||||
|
pub struct H3CellContains;
|
||||||
|
|
||||||
|
impl Function for H3CellContains {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"h3_cells_contains"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
|
||||||
|
Ok(ConcreteDataType::boolean_datatype())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
let multi_cell_types = vec![
|
||||||
|
ConcreteDataType::list_datatype(ConcreteDataType::int64_datatype()),
|
||||||
|
ConcreteDataType::list_datatype(ConcreteDataType::uint64_datatype()),
|
||||||
|
ConcreteDataType::list_datatype(ConcreteDataType::string_datatype()),
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut signatures = Vec::with_capacity(multi_cell_types.len() * CELL_TYPES.len());
|
||||||
|
for multi_cell_type in &multi_cell_types {
|
||||||
|
for cell_type in CELL_TYPES.as_slice() {
|
||||||
|
signatures.push(TypeSignature::Exact(vec![
|
||||||
|
multi_cell_type.clone(),
|
||||||
|
cell_type.clone(),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Signature::one_of(signatures, Volatility::Stable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(&self, _func_ctx: FunctionContext, columns: &[VectorRef]) -> Result<VectorRef> {
|
||||||
|
ensure_columns_n!(columns, 2);
|
||||||
|
|
||||||
|
let cells_vec = &columns[0];
|
||||||
|
let cell_this_vec = &columns[1];
|
||||||
|
|
||||||
|
let size = cell_this_vec.len();
|
||||||
|
let mut results = BooleanVectorBuilder::with_capacity(size);
|
||||||
|
|
||||||
|
for i in 0..size {
|
||||||
|
let mut result = None;
|
||||||
|
if let (cells, Some(cell_this)) = (
|
||||||
|
cells_from_value(cells_vec.get(i))?,
|
||||||
|
cell_from_value(cell_this_vec.get(i))?,
|
||||||
|
) {
|
||||||
|
result = Some(false);
|
||||||
|
|
||||||
|
for cell_that in cells.iter() {
|
||||||
|
// get cell resolution, and find cell_this's parent at
|
||||||
|
// this solution, test if cell_that equals the parent
|
||||||
|
let resolution = cell_that.resolution();
|
||||||
|
if let Some(cell_this_parent) = cell_this.parent(resolution) {
|
||||||
|
if cell_this_parent == *cell_that {
|
||||||
|
result = Some(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(results.to_vector())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get WGS84 great circle distance of two cell centroid
|
||||||
|
#[derive(Clone, Debug, Default, Display)]
|
||||||
|
#[display("{}", self.name())]
|
||||||
|
pub struct H3CellDistanceSphereKm;
|
||||||
|
|
||||||
|
impl Function for H3CellDistanceSphereKm {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"h3_distance_sphere_km"
|
||||||
|
}
|
||||||
|
fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
|
||||||
|
Ok(ConcreteDataType::float64_datatype())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
signature_of_double_cells()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(&self, _func_ctx: FunctionContext, columns: &[VectorRef]) -> Result<VectorRef> {
|
||||||
|
ensure_columns_n!(columns, 2);
|
||||||
|
|
||||||
|
let cell_this_vec = &columns[0];
|
||||||
|
let cell_that_vec = &columns[1];
|
||||||
|
let size = cell_this_vec.len();
|
||||||
|
|
||||||
|
let mut results = Float64VectorBuilder::with_capacity(size);
|
||||||
|
|
||||||
|
for i in 0..size {
|
||||||
|
let result = match (
|
||||||
|
cell_from_value(cell_this_vec.get(i))?,
|
||||||
|
cell_from_value(cell_that_vec.get(i))?,
|
||||||
|
) {
|
||||||
|
(Some(cell_this), Some(cell_that)) => {
|
||||||
|
let centroid_this = LatLng::from(cell_this);
|
||||||
|
let centroid_that = LatLng::from(cell_that);
|
||||||
|
|
||||||
|
Some(centroid_this.distance_km(centroid_that))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
results.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(results.to_vector())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get Euclidean distance of two cell centroid
|
||||||
|
#[derive(Clone, Debug, Default, Display)]
|
||||||
|
#[display("{}", self.name())]
|
||||||
|
pub struct H3CellDistanceEuclideanDegree;
|
||||||
|
|
||||||
|
impl H3CellDistanceEuclideanDegree {
|
||||||
|
fn distance(centroid_this: LatLng, centroid_that: LatLng) -> f64 {
|
||||||
|
((centroid_this.lat() - centroid_that.lat()).powi(2)
|
||||||
|
+ (centroid_this.lng() - centroid_that.lng()).powi(2))
|
||||||
|
.sqrt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Function for H3CellDistanceEuclideanDegree {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"h3_distance_degree"
|
||||||
|
}
|
||||||
|
fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
|
||||||
|
Ok(ConcreteDataType::float64_datatype())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
signature_of_double_cells()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(&self, _func_ctx: FunctionContext, columns: &[VectorRef]) -> Result<VectorRef> {
|
||||||
|
ensure_columns_n!(columns, 2);
|
||||||
|
|
||||||
|
let cell_this_vec = &columns[0];
|
||||||
|
let cell_that_vec = &columns[1];
|
||||||
|
let size = cell_this_vec.len();
|
||||||
|
|
||||||
|
let mut results = Float64VectorBuilder::with_capacity(size);
|
||||||
|
|
||||||
|
for i in 0..size {
|
||||||
|
let result = match (
|
||||||
|
cell_from_value(cell_this_vec.get(i))?,
|
||||||
|
cell_from_value(cell_that_vec.get(i))?,
|
||||||
|
) {
|
||||||
|
(Some(cell_this), Some(cell_that)) => {
|
||||||
|
let centroid_this = LatLng::from(cell_this);
|
||||||
|
let centroid_that = LatLng::from(cell_that);
|
||||||
|
|
||||||
|
let dist = Self::distance(centroid_this, centroid_that);
|
||||||
|
Some(dist)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
results.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(results.to_vector())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn value_to_resolution(v: Value) -> Result<Resolution> {
|
fn value_to_resolution(v: Value) -> Result<Resolution> {
|
||||||
let r = match v {
|
let r = match v {
|
||||||
Value::Int8(v) => v as u8,
|
Value::Int8(v) => v as u8,
|
||||||
@@ -1073,7 +1249,126 @@ fn cell_from_value(v: Value) -> Result<Option<CellIndex>> {
|
|||||||
})
|
})
|
||||||
.context(error::ExecuteSnafu)?,
|
.context(error::ExecuteSnafu)?,
|
||||||
),
|
),
|
||||||
|
Value::String(s) => Some(
|
||||||
|
CellIndex::from_str(s.as_utf8())
|
||||||
|
.map_err(|e| {
|
||||||
|
BoxedError::new(PlainError::new(
|
||||||
|
format!("H3 error: {}", e),
|
||||||
|
StatusCode::EngineExecuteQuery,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.context(error::ExecuteSnafu)?,
|
||||||
|
),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
Ok(cell)
|
Ok(cell)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// extract cell array from all possible types including:
|
||||||
|
/// - int64 list
|
||||||
|
/// - uint64 list
|
||||||
|
/// - string list
|
||||||
|
/// - comma-separated string
|
||||||
|
fn cells_from_value(v: Value) -> Result<Vec<CellIndex>> {
|
||||||
|
match v {
|
||||||
|
Value::List(list) => match list.datatype() {
|
||||||
|
ConcreteDataType::Int64(_) => list
|
||||||
|
.items()
|
||||||
|
.iter()
|
||||||
|
.map(|v| {
|
||||||
|
if let Value::Int64(v) = v {
|
||||||
|
CellIndex::try_from(*v as u64)
|
||||||
|
.map_err(|e| {
|
||||||
|
BoxedError::new(PlainError::new(
|
||||||
|
format!("H3 error: {}", e),
|
||||||
|
StatusCode::EngineExecuteQuery,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.context(error::ExecuteSnafu)
|
||||||
|
} else {
|
||||||
|
Err(BoxedError::new(PlainError::new(
|
||||||
|
"Invalid data type in array".to_string(),
|
||||||
|
StatusCode::EngineExecuteQuery,
|
||||||
|
)))
|
||||||
|
.context(error::ExecuteSnafu)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<CellIndex>>>(),
|
||||||
|
ConcreteDataType::UInt64(_) => list
|
||||||
|
.items()
|
||||||
|
.iter()
|
||||||
|
.map(|v| {
|
||||||
|
if let Value::UInt64(v) = v {
|
||||||
|
CellIndex::try_from(*v)
|
||||||
|
.map_err(|e| {
|
||||||
|
BoxedError::new(PlainError::new(
|
||||||
|
format!("H3 error: {}", e),
|
||||||
|
StatusCode::EngineExecuteQuery,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.context(error::ExecuteSnafu)
|
||||||
|
} else {
|
||||||
|
Err(BoxedError::new(PlainError::new(
|
||||||
|
"Invalid data type in array".to_string(),
|
||||||
|
StatusCode::EngineExecuteQuery,
|
||||||
|
)))
|
||||||
|
.context(error::ExecuteSnafu)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<CellIndex>>>(),
|
||||||
|
ConcreteDataType::String(_) => list
|
||||||
|
.items()
|
||||||
|
.iter()
|
||||||
|
.map(|v| {
|
||||||
|
if let Value::String(v) = v {
|
||||||
|
CellIndex::from_str(v.as_utf8().trim())
|
||||||
|
.map_err(|e| {
|
||||||
|
BoxedError::new(PlainError::new(
|
||||||
|
format!("H3 error: {}", e),
|
||||||
|
StatusCode::EngineExecuteQuery,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.context(error::ExecuteSnafu)
|
||||||
|
} else {
|
||||||
|
Err(BoxedError::new(PlainError::new(
|
||||||
|
"Invalid data type in array".to_string(),
|
||||||
|
StatusCode::EngineExecuteQuery,
|
||||||
|
)))
|
||||||
|
.context(error::ExecuteSnafu)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<CellIndex>>>(),
|
||||||
|
_ => Ok(vec![]),
|
||||||
|
},
|
||||||
|
Value::String(csv) => {
|
||||||
|
let str_seq = csv.as_utf8().split(',');
|
||||||
|
str_seq
|
||||||
|
.map(|v| {
|
||||||
|
CellIndex::from_str(v.trim())
|
||||||
|
.map_err(|e| {
|
||||||
|
BoxedError::new(PlainError::new(
|
||||||
|
format!("H3 error: {}", e),
|
||||||
|
StatusCode::EngineExecuteQuery,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.context(error::ExecuteSnafu)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<CellIndex>>>()
|
||||||
|
}
|
||||||
|
_ => Ok(vec![]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h3_euclidean_distance() {
|
||||||
|
let point_this = LatLng::new(42.3521, -72.1235).expect("incorrect lat lng");
|
||||||
|
let point_that = LatLng::new(42.45, -72.1260).expect("incorrect lat lng");
|
||||||
|
|
||||||
|
let dist = H3CellDistanceEuclideanDegree::distance(point_this, point_that);
|
||||||
|
assert_eq!(dist, 0.09793191512474639);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
195
src/common/function/src/scalars/geo/measure.rs
Normal file
195
src/common/function/src/scalars/geo/measure.rs
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
// Copyright 2023 Greptime Team
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use common_error::ext::{BoxedError, PlainError};
|
||||||
|
use common_error::status_code::StatusCode;
|
||||||
|
use common_query::error::{self, Result};
|
||||||
|
use common_query::prelude::{Signature, TypeSignature};
|
||||||
|
use datafusion::logical_expr::Volatility;
|
||||||
|
use datatypes::prelude::ConcreteDataType;
|
||||||
|
use datatypes::scalars::ScalarVectorBuilder;
|
||||||
|
use datatypes::vectors::{Float64VectorBuilder, MutableVector, VectorRef};
|
||||||
|
use derive_more::Display;
|
||||||
|
use geo::algorithm::line_measures::metric_spaces::Euclidean;
|
||||||
|
use geo::{Area, Distance, Haversine};
|
||||||
|
use geo_types::Geometry;
|
||||||
|
use snafu::ResultExt;
|
||||||
|
|
||||||
|
use super::helpers::{ensure_columns_len, ensure_columns_n};
|
||||||
|
use super::wkt::parse_wkt;
|
||||||
|
use crate::function::{Function, FunctionContext};
|
||||||
|
|
||||||
|
/// Return WGS84(SRID: 4326) euclidean distance between two geometry object, in degree
|
||||||
|
#[derive(Clone, Debug, Default, Display)]
|
||||||
|
#[display("{}", self.name())]
|
||||||
|
pub struct STDistance;
|
||||||
|
|
||||||
|
impl Function for STDistance {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"st_distance"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
|
||||||
|
Ok(ConcreteDataType::float64_datatype())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::new(
|
||||||
|
TypeSignature::Exact(vec![
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
]),
|
||||||
|
Volatility::Stable,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(&self, _func_ctx: FunctionContext, columns: &[VectorRef]) -> Result<VectorRef> {
|
||||||
|
ensure_columns_n!(columns, 2);
|
||||||
|
|
||||||
|
let wkt_this_vec = &columns[0];
|
||||||
|
let wkt_that_vec = &columns[1];
|
||||||
|
|
||||||
|
let size = wkt_this_vec.len();
|
||||||
|
let mut results = Float64VectorBuilder::with_capacity(size);
|
||||||
|
|
||||||
|
for i in 0..size {
|
||||||
|
let wkt_this = wkt_this_vec.get(i).as_string();
|
||||||
|
let wkt_that = wkt_that_vec.get(i).as_string();
|
||||||
|
|
||||||
|
let result = match (wkt_this, wkt_that) {
|
||||||
|
(Some(wkt_this), Some(wkt_that)) => {
|
||||||
|
let geom_this = parse_wkt(&wkt_this)?;
|
||||||
|
let geom_that = parse_wkt(&wkt_that)?;
|
||||||
|
|
||||||
|
Some(Euclidean::distance(&geom_this, &geom_that))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
results.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(results.to_vector())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return great circle distance between two geometry object, in meters
|
||||||
|
#[derive(Clone, Debug, Default, Display)]
|
||||||
|
#[display("{}", self.name())]
|
||||||
|
pub struct STDistanceSphere;
|
||||||
|
|
||||||
|
impl Function for STDistanceSphere {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"st_distance_sphere_m"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
|
||||||
|
Ok(ConcreteDataType::float64_datatype())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::new(
|
||||||
|
TypeSignature::Exact(vec![
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
]),
|
||||||
|
Volatility::Stable,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(&self, _func_ctx: FunctionContext, columns: &[VectorRef]) -> Result<VectorRef> {
|
||||||
|
ensure_columns_n!(columns, 2);
|
||||||
|
|
||||||
|
let wkt_this_vec = &columns[0];
|
||||||
|
let wkt_that_vec = &columns[1];
|
||||||
|
|
||||||
|
let size = wkt_this_vec.len();
|
||||||
|
let mut results = Float64VectorBuilder::with_capacity(size);
|
||||||
|
|
||||||
|
for i in 0..size {
|
||||||
|
let wkt_this = wkt_this_vec.get(i).as_string();
|
||||||
|
let wkt_that = wkt_that_vec.get(i).as_string();
|
||||||
|
|
||||||
|
let result = match (wkt_this, wkt_that) {
|
||||||
|
(Some(wkt_this), Some(wkt_that)) => {
|
||||||
|
let geom_this = parse_wkt(&wkt_this)?;
|
||||||
|
let geom_that = parse_wkt(&wkt_that)?;
|
||||||
|
|
||||||
|
match (geom_this, geom_that) {
|
||||||
|
(Geometry::Point(this), Geometry::Point(that)) => {
|
||||||
|
Some(Haversine::distance(this, that))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
Err(BoxedError::new(PlainError::new(
|
||||||
|
"Great circle distance between non-point objects are not supported for now.".to_string(),
|
||||||
|
StatusCode::Unsupported,
|
||||||
|
))).context(error::ExecuteSnafu)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
results.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(results.to_vector())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return area of given geometry object
|
||||||
|
#[derive(Clone, Debug, Default, Display)]
|
||||||
|
#[display("{}", self.name())]
|
||||||
|
pub struct STArea;
|
||||||
|
|
||||||
|
impl Function for STArea {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"st_area"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
|
||||||
|
Ok(ConcreteDataType::float64_datatype())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::new(
|
||||||
|
TypeSignature::Exact(vec![ConcreteDataType::string_datatype()]),
|
||||||
|
Volatility::Stable,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(&self, _func_ctx: FunctionContext, columns: &[VectorRef]) -> Result<VectorRef> {
|
||||||
|
ensure_columns_n!(columns, 1);
|
||||||
|
|
||||||
|
let wkt_vec = &columns[0];
|
||||||
|
|
||||||
|
let size = wkt_vec.len();
|
||||||
|
let mut results = Float64VectorBuilder::with_capacity(size);
|
||||||
|
|
||||||
|
for i in 0..size {
|
||||||
|
let wkt = wkt_vec.get(i).as_string();
|
||||||
|
|
||||||
|
let result = if let Some(wkt) = wkt {
|
||||||
|
let geom = parse_wkt(&wkt)?;
|
||||||
|
Some(geom.unsigned_area())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
results.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(results.to_vector())
|
||||||
|
}
|
||||||
|
}
|
||||||
190
src/common/function/src/scalars/geo/relation.rs
Normal file
190
src/common/function/src/scalars/geo/relation.rs
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
// Copyright 2023 Greptime Team
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use common_query::error::Result;
|
||||||
|
use common_query::prelude::{Signature, TypeSignature};
|
||||||
|
use datafusion::logical_expr::Volatility;
|
||||||
|
use datatypes::prelude::ConcreteDataType;
|
||||||
|
use datatypes::scalars::ScalarVectorBuilder;
|
||||||
|
use datatypes::vectors::{BooleanVectorBuilder, MutableVector, VectorRef};
|
||||||
|
use derive_more::Display;
|
||||||
|
use geo::algorithm::contains::Contains;
|
||||||
|
use geo::algorithm::intersects::Intersects;
|
||||||
|
use geo::algorithm::within::Within;
|
||||||
|
|
||||||
|
use super::helpers::{ensure_columns_len, ensure_columns_n};
|
||||||
|
use super::wkt::parse_wkt;
|
||||||
|
use crate::function::{Function, FunctionContext};
|
||||||
|
|
||||||
|
/// Test if spatial relationship: contains
|
||||||
|
#[derive(Clone, Debug, Default, Display)]
|
||||||
|
#[display("{}", self.name())]
|
||||||
|
pub struct STContains;
|
||||||
|
|
||||||
|
impl Function for STContains {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"st_contains"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
|
||||||
|
Ok(ConcreteDataType::boolean_datatype())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::new(
|
||||||
|
TypeSignature::Exact(vec![
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
]),
|
||||||
|
Volatility::Stable,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(&self, _func_ctx: FunctionContext, columns: &[VectorRef]) -> Result<VectorRef> {
|
||||||
|
ensure_columns_n!(columns, 2);
|
||||||
|
|
||||||
|
let wkt_this_vec = &columns[0];
|
||||||
|
let wkt_that_vec = &columns[1];
|
||||||
|
|
||||||
|
let size = wkt_this_vec.len();
|
||||||
|
let mut results = BooleanVectorBuilder::with_capacity(size);
|
||||||
|
|
||||||
|
for i in 0..size {
|
||||||
|
let wkt_this = wkt_this_vec.get(i).as_string();
|
||||||
|
let wkt_that = wkt_that_vec.get(i).as_string();
|
||||||
|
|
||||||
|
let result = match (wkt_this, wkt_that) {
|
||||||
|
(Some(wkt_this), Some(wkt_that)) => {
|
||||||
|
let geom_this = parse_wkt(&wkt_this)?;
|
||||||
|
let geom_that = parse_wkt(&wkt_that)?;
|
||||||
|
|
||||||
|
Some(geom_this.contains(&geom_that))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
results.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(results.to_vector())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test if spatial relationship: within
|
||||||
|
#[derive(Clone, Debug, Default, Display)]
|
||||||
|
#[display("{}", self.name())]
|
||||||
|
pub struct STWithin;
|
||||||
|
|
||||||
|
impl Function for STWithin {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"st_within"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
|
||||||
|
Ok(ConcreteDataType::boolean_datatype())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::new(
|
||||||
|
TypeSignature::Exact(vec![
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
]),
|
||||||
|
Volatility::Stable,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(&self, _func_ctx: FunctionContext, columns: &[VectorRef]) -> Result<VectorRef> {
|
||||||
|
ensure_columns_n!(columns, 2);
|
||||||
|
|
||||||
|
let wkt_this_vec = &columns[0];
|
||||||
|
let wkt_that_vec = &columns[1];
|
||||||
|
|
||||||
|
let size = wkt_this_vec.len();
|
||||||
|
let mut results = BooleanVectorBuilder::with_capacity(size);
|
||||||
|
|
||||||
|
for i in 0..size {
|
||||||
|
let wkt_this = wkt_this_vec.get(i).as_string();
|
||||||
|
let wkt_that = wkt_that_vec.get(i).as_string();
|
||||||
|
|
||||||
|
let result = match (wkt_this, wkt_that) {
|
||||||
|
(Some(wkt_this), Some(wkt_that)) => {
|
||||||
|
let geom_this = parse_wkt(&wkt_this)?;
|
||||||
|
let geom_that = parse_wkt(&wkt_that)?;
|
||||||
|
|
||||||
|
Some(geom_this.is_within(&geom_that))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
results.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(results.to_vector())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test if spatial relationship: within
|
||||||
|
#[derive(Clone, Debug, Default, Display)]
|
||||||
|
#[display("{}", self.name())]
|
||||||
|
pub struct STIntersects;
|
||||||
|
|
||||||
|
impl Function for STIntersects {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"st_intersects"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
|
||||||
|
Ok(ConcreteDataType::boolean_datatype())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::new(
|
||||||
|
TypeSignature::Exact(vec![
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
]),
|
||||||
|
Volatility::Stable,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(&self, _func_ctx: FunctionContext, columns: &[VectorRef]) -> Result<VectorRef> {
|
||||||
|
ensure_columns_n!(columns, 2);
|
||||||
|
|
||||||
|
let wkt_this_vec = &columns[0];
|
||||||
|
let wkt_that_vec = &columns[1];
|
||||||
|
|
||||||
|
let size = wkt_this_vec.len();
|
||||||
|
let mut results = BooleanVectorBuilder::with_capacity(size);
|
||||||
|
|
||||||
|
for i in 0..size {
|
||||||
|
let wkt_this = wkt_this_vec.get(i).as_string();
|
||||||
|
let wkt_that = wkt_that_vec.get(i).as_string();
|
||||||
|
|
||||||
|
let result = match (wkt_this, wkt_that) {
|
||||||
|
(Some(wkt_this), Some(wkt_that)) => {
|
||||||
|
let geom_this = parse_wkt(&wkt_this)?;
|
||||||
|
let geom_that = parse_wkt(&wkt_that)?;
|
||||||
|
|
||||||
|
Some(geom_this.intersects(&geom_that))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
results.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(results.to_vector())
|
||||||
|
}
|
||||||
|
}
|
||||||
100
src/common/function/src/scalars/geo/wkt.rs
Normal file
100
src/common/function/src/scalars/geo/wkt.rs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// Copyright 2023 Greptime Team
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use common_error::ext::{BoxedError, PlainError};
|
||||||
|
use common_error::status_code::StatusCode;
|
||||||
|
use common_query::error::{self, Result};
|
||||||
|
use common_query::prelude::{Signature, TypeSignature};
|
||||||
|
use datafusion::logical_expr::Volatility;
|
||||||
|
use datatypes::prelude::ConcreteDataType;
|
||||||
|
use datatypes::scalars::ScalarVectorBuilder;
|
||||||
|
use datatypes::vectors::{MutableVector, StringVectorBuilder, VectorRef};
|
||||||
|
use derive_more::Display;
|
||||||
|
use geo_types::{Geometry, Point};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use snafu::ResultExt;
|
||||||
|
use wkt::{ToWkt, TryFromWkt};
|
||||||
|
|
||||||
|
use super::helpers::{ensure_columns_len, ensure_columns_n};
|
||||||
|
use crate::function::{Function, FunctionContext};
|
||||||
|
|
||||||
|
static COORDINATE_TYPES: Lazy<Vec<ConcreteDataType>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
ConcreteDataType::float32_datatype(),
|
||||||
|
ConcreteDataType::float64_datatype(),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Return WGS84(SRID: 4326) euclidean distance between two geometry object, in degree
|
||||||
|
#[derive(Clone, Debug, Default, Display)]
|
||||||
|
#[display("{}", self.name())]
|
||||||
|
pub struct LatLngToPointWkt;
|
||||||
|
|
||||||
|
impl Function for LatLngToPointWkt {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"wkt_point_from_latlng"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
|
||||||
|
Ok(ConcreteDataType::string_datatype())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
let mut signatures = Vec::new();
|
||||||
|
for coord_type in COORDINATE_TYPES.as_slice() {
|
||||||
|
signatures.push(TypeSignature::Exact(vec![
|
||||||
|
// latitude
|
||||||
|
coord_type.clone(),
|
||||||
|
// longitude
|
||||||
|
coord_type.clone(),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
Signature::one_of(signatures, Volatility::Stable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(&self, _func_ctx: FunctionContext, columns: &[VectorRef]) -> Result<VectorRef> {
|
||||||
|
ensure_columns_n!(columns, 2);
|
||||||
|
|
||||||
|
let lat_vec = &columns[0];
|
||||||
|
let lng_vec = &columns[1];
|
||||||
|
|
||||||
|
let size = lat_vec.len();
|
||||||
|
let mut results = StringVectorBuilder::with_capacity(size);
|
||||||
|
|
||||||
|
for i in 0..size {
|
||||||
|
let lat = lat_vec.get(i).as_f64_lossy();
|
||||||
|
let lng = lng_vec.get(i).as_f64_lossy();
|
||||||
|
|
||||||
|
let result = match (lat, lng) {
|
||||||
|
(Some(lat), Some(lng)) => Some(Point::new(lng, lat).wkt_string()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
results.push(result.as_deref());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(results.to_vector())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn parse_wkt(s: &str) -> Result<Geometry> {
|
||||||
|
Geometry::try_from_wkt_str(s)
|
||||||
|
.map_err(|e| {
|
||||||
|
BoxedError::new(PlainError::new(
|
||||||
|
format!("Fail to parse WKT: {}", e),
|
||||||
|
StatusCode::EngineExecuteQuery,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.context(error::ExecuteSnafu)
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
|
|
||||||
use common_query::error::{InvalidFuncArgsSnafu, Result, UnsupportedInputDataTypeSnafu};
|
use common_query::error::{InvalidFuncArgsSnafu, Result, UnsupportedInputDataTypeSnafu};
|
||||||
use common_query::prelude::Signature;
|
use common_query::prelude::{Signature, TypeSignature};
|
||||||
use datafusion::logical_expr::Volatility;
|
use datafusion::logical_expr::Volatility;
|
||||||
use datatypes::data_type::ConcreteDataType;
|
use datatypes::data_type::ConcreteDataType;
|
||||||
use datatypes::prelude::VectorRef;
|
use datatypes::prelude::VectorRef;
|
||||||
@@ -41,10 +41,24 @@ impl Function for JsonPathExistsFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::exact(
|
Signature::one_of(
|
||||||
vec![
|
vec![
|
||||||
ConcreteDataType::json_datatype(),
|
TypeSignature::Exact(vec![
|
||||||
ConcreteDataType::string_datatype(),
|
ConcreteDataType::json_datatype(),
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
]),
|
||||||
|
TypeSignature::Exact(vec![
|
||||||
|
ConcreteDataType::null_datatype(),
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
]),
|
||||||
|
TypeSignature::Exact(vec![
|
||||||
|
ConcreteDataType::json_datatype(),
|
||||||
|
ConcreteDataType::null_datatype(),
|
||||||
|
]),
|
||||||
|
TypeSignature::Exact(vec![
|
||||||
|
ConcreteDataType::null_datatype(),
|
||||||
|
ConcreteDataType::null_datatype(),
|
||||||
|
]),
|
||||||
],
|
],
|
||||||
Volatility::Immutable,
|
Volatility::Immutable,
|
||||||
)
|
)
|
||||||
@@ -64,25 +78,26 @@ impl Function for JsonPathExistsFunction {
|
|||||||
let paths = &columns[1];
|
let paths = &columns[1];
|
||||||
|
|
||||||
let size = jsons.len();
|
let size = jsons.len();
|
||||||
let datatype = jsons.data_type();
|
|
||||||
let mut results = BooleanVectorBuilder::with_capacity(size);
|
let mut results = BooleanVectorBuilder::with_capacity(size);
|
||||||
|
|
||||||
match datatype {
|
match (jsons.data_type(), paths.data_type()) {
|
||||||
// JSON data type uses binary vector
|
(ConcreteDataType::Binary(_), ConcreteDataType::String(_)) => {
|
||||||
ConcreteDataType::Binary(_) => {
|
|
||||||
for i in 0..size {
|
for i in 0..size {
|
||||||
let json = jsons.get_ref(i);
|
let result = match (jsons.get_ref(i).as_binary(), paths.get_ref(i).as_string())
|
||||||
let path = paths.get_ref(i);
|
{
|
||||||
|
|
||||||
let json = json.as_binary();
|
|
||||||
let path = path.as_string();
|
|
||||||
let result = match (json, path) {
|
|
||||||
(Ok(Some(json)), Ok(Some(path))) => {
|
(Ok(Some(json)), Ok(Some(path))) => {
|
||||||
let json_path = jsonb::jsonpath::parse_json_path(path.as_bytes());
|
// Get `JsonPath`.
|
||||||
match json_path {
|
let json_path = match jsonb::jsonpath::parse_json_path(path.as_bytes())
|
||||||
Ok(json_path) => jsonb::path_exists(json, json_path).ok(),
|
{
|
||||||
Err(_) => None,
|
Ok(json_path) => json_path,
|
||||||
}
|
Err(_) => {
|
||||||
|
return InvalidFuncArgsSnafu {
|
||||||
|
err_msg: format!("Illegal json path: {:?}", path),
|
||||||
|
}
|
||||||
|
.fail();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
jsonb::path_exists(json, json_path).ok()
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
@@ -90,6 +105,12 @@ impl Function for JsonPathExistsFunction {
|
|||||||
results.push(result);
|
results.push(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Any null args existence causes the result to be NULL.
|
||||||
|
(ConcreteDataType::Null(_), ConcreteDataType::String(_)) => results.push_nulls(size),
|
||||||
|
(ConcreteDataType::Binary(_), ConcreteDataType::Null(_)) => results.push_nulls(size),
|
||||||
|
(ConcreteDataType::Null(_), ConcreteDataType::Null(_)) => results.push_nulls(size),
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
return UnsupportedInputDataTypeSnafu {
|
return UnsupportedInputDataTypeSnafu {
|
||||||
function: NAME,
|
function: NAME,
|
||||||
@@ -114,8 +135,8 @@ mod tests {
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use common_query::prelude::TypeSignature;
|
use common_query::prelude::TypeSignature;
|
||||||
use datatypes::scalars::ScalarVector;
|
use datatypes::prelude::ScalarVector;
|
||||||
use datatypes::vectors::{BinaryVector, StringVector};
|
use datatypes::vectors::{BinaryVector, NullVector, StringVector};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -133,9 +154,27 @@ mod tests {
|
|||||||
|
|
||||||
assert!(matches!(json_path_exists.signature(),
|
assert!(matches!(json_path_exists.signature(),
|
||||||
Signature {
|
Signature {
|
||||||
type_signature: TypeSignature::Exact(valid_types),
|
type_signature: TypeSignature::OneOf(valid_types),
|
||||||
volatility: Volatility::Immutable
|
volatility: Volatility::Immutable
|
||||||
} if valid_types == vec![ConcreteDataType::json_datatype(), ConcreteDataType::string_datatype()]
|
} if valid_types ==
|
||||||
|
vec![
|
||||||
|
TypeSignature::Exact(vec![
|
||||||
|
ConcreteDataType::json_datatype(),
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
]),
|
||||||
|
TypeSignature::Exact(vec![
|
||||||
|
ConcreteDataType::null_datatype(),
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
]),
|
||||||
|
TypeSignature::Exact(vec![
|
||||||
|
ConcreteDataType::json_datatype(),
|
||||||
|
ConcreteDataType::null_datatype(),
|
||||||
|
]),
|
||||||
|
TypeSignature::Exact(vec![
|
||||||
|
ConcreteDataType::null_datatype(),
|
||||||
|
ConcreteDataType::null_datatype(),
|
||||||
|
]),
|
||||||
|
],
|
||||||
));
|
));
|
||||||
|
|
||||||
let json_strings = [
|
let json_strings = [
|
||||||
@@ -143,9 +182,15 @@ mod tests {
|
|||||||
r#"{"a": 4, "b": {"c": 6}, "c": 6}"#,
|
r#"{"a": 4, "b": {"c": 6}, "c": 6}"#,
|
||||||
r#"{"a": 7, "b": 8, "c": {"a": 7}}"#,
|
r#"{"a": 7, "b": 8, "c": {"a": 7}}"#,
|
||||||
r#"{"a": 7, "b": 8, "c": {"a": 7}}"#,
|
r#"{"a": 7, "b": 8, "c": {"a": 7}}"#,
|
||||||
|
r#"[1, 2, 3]"#,
|
||||||
|
r#"null"#,
|
||||||
|
r#"{"a": 7, "b": 8, "c": {"a": 7}}"#,
|
||||||
|
r#"null"#,
|
||||||
];
|
];
|
||||||
let paths = vec!["$.a.b.c", "$.b", "$.c.a", ".d"];
|
let paths = vec![
|
||||||
let results = [false, true, true, false];
|
"$.a.b.c", "$.b", "$.c.a", ".d", "$[0]", "$.a", "null", "null",
|
||||||
|
];
|
||||||
|
let expected = [false, true, true, false, true, false, false, false];
|
||||||
|
|
||||||
let jsonbs = json_strings
|
let jsonbs = json_strings
|
||||||
.iter()
|
.iter()
|
||||||
@@ -162,11 +207,44 @@ mod tests {
|
|||||||
.eval(FunctionContext::default(), &args)
|
.eval(FunctionContext::default(), &args)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(4, vector.len());
|
// Test for non-nulls.
|
||||||
for (i, gt) in results.iter().enumerate() {
|
assert_eq!(8, vector.len());
|
||||||
|
for (i, real) in expected.iter().enumerate() {
|
||||||
let result = vector.get_ref(i);
|
let result = vector.get_ref(i);
|
||||||
let result = result.as_boolean().unwrap().unwrap();
|
assert!(!result.is_null());
|
||||||
assert_eq!(*gt, result);
|
let val = result.as_boolean().unwrap().unwrap();
|
||||||
|
assert_eq!(val, *real);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test for path error.
|
||||||
|
let json_bytes = jsonb::parse_value("{}".as_bytes()).unwrap().to_vec();
|
||||||
|
let json = BinaryVector::from_vec(vec![json_bytes]);
|
||||||
|
let illegal_path = StringVector::from_vec(vec!["$..a"]);
|
||||||
|
|
||||||
|
let args: Vec<VectorRef> = vec![Arc::new(json), Arc::new(illegal_path)];
|
||||||
|
let err = json_path_exists.eval(FunctionContext::default(), &args);
|
||||||
|
assert!(err.is_err());
|
||||||
|
|
||||||
|
// Test for nulls.
|
||||||
|
let json_bytes = jsonb::parse_value("{}".as_bytes()).unwrap().to_vec();
|
||||||
|
let json = BinaryVector::from_vec(vec![json_bytes]);
|
||||||
|
let null_json = NullVector::new(1);
|
||||||
|
|
||||||
|
let path = StringVector::from_vec(vec!["$.a"]);
|
||||||
|
let null_path = NullVector::new(1);
|
||||||
|
|
||||||
|
let args: Vec<VectorRef> = vec![Arc::new(null_json), Arc::new(path)];
|
||||||
|
let result1 = json_path_exists
|
||||||
|
.eval(FunctionContext::default(), &args)
|
||||||
|
.unwrap();
|
||||||
|
let args: Vec<VectorRef> = vec![Arc::new(json), Arc::new(null_path)];
|
||||||
|
let result2 = json_path_exists
|
||||||
|
.eval(FunctionContext::default(), &args)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(result1.len(), 1);
|
||||||
|
assert!(result1.get_ref(0).is_null());
|
||||||
|
assert_eq!(result2.len(), 1);
|
||||||
|
assert!(result2.get_ref(0).is_null());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,20 +15,22 @@
|
|||||||
use api::helper::ColumnDataTypeWrapper;
|
use api::helper::ColumnDataTypeWrapper;
|
||||||
use api::v1::add_column_location::LocationType;
|
use api::v1::add_column_location::LocationType;
|
||||||
use api::v1::alter_expr::Kind;
|
use api::v1::alter_expr::Kind;
|
||||||
|
use api::v1::column_def::as_fulltext_option;
|
||||||
use api::v1::{
|
use api::v1::{
|
||||||
column_def, AddColumnLocation as Location, AlterExpr, ChangeColumnTypes, CreateTableExpr,
|
column_def, AddColumnLocation as Location, AlterExpr, Analyzer, ChangeColumnTypes,
|
||||||
DropColumns, RenameTable, SemanticType,
|
CreateTableExpr, DropColumns, RenameTable, SemanticType,
|
||||||
};
|
};
|
||||||
use common_query::AddColumnLocation;
|
use common_query::AddColumnLocation;
|
||||||
use datatypes::schema::{ColumnSchema, RawSchema};
|
use datatypes::schema::{ColumnSchema, FulltextOptions, RawSchema};
|
||||||
use snafu::{ensure, OptionExt, ResultExt};
|
use snafu::{ensure, OptionExt, ResultExt};
|
||||||
use store_api::region_request::ChangeOption;
|
use store_api::region_request::ChangeOption;
|
||||||
use table::metadata::TableId;
|
use table::metadata::TableId;
|
||||||
use table::requests::{AddColumnRequest, AlterKind, AlterTableRequest, ChangeColumnTypeRequest};
|
use table::requests::{AddColumnRequest, AlterKind, AlterTableRequest, ChangeColumnTypeRequest};
|
||||||
|
|
||||||
use crate::error::{
|
use crate::error::{
|
||||||
InvalidChangeTableOptionRequestSnafu, InvalidColumnDefSnafu, MissingFieldSnafu,
|
InvalidChangeFulltextOptionRequestSnafu, InvalidChangeTableOptionRequestSnafu,
|
||||||
MissingTimestampColumnSnafu, Result, UnknownLocationTypeSnafu,
|
InvalidColumnDefSnafu, MissingFieldSnafu, MissingTimestampColumnSnafu, Result,
|
||||||
|
UnknownLocationTypeSnafu,
|
||||||
};
|
};
|
||||||
|
|
||||||
const LOCATION_TYPE_FIRST: i32 = LocationType::First as i32;
|
const LOCATION_TYPE_FIRST: i32 = LocationType::First as i32;
|
||||||
@@ -102,6 +104,17 @@ pub fn alter_expr_to_request(table_id: TableId, expr: AlterExpr) -> Result<Alter
|
|||||||
.collect::<std::result::Result<Vec<_>, _>>()
|
.collect::<std::result::Result<Vec<_>, _>>()
|
||||||
.context(InvalidChangeTableOptionRequestSnafu)?,
|
.context(InvalidChangeTableOptionRequestSnafu)?,
|
||||||
},
|
},
|
||||||
|
Kind::ChangeColumnFulltext(c) => AlterKind::ChangeColumnFulltext {
|
||||||
|
column_name: c.column_name,
|
||||||
|
options: FulltextOptions {
|
||||||
|
enable: c.enable,
|
||||||
|
analyzer: as_fulltext_option(
|
||||||
|
Analyzer::try_from(c.analyzer)
|
||||||
|
.context(InvalidChangeFulltextOptionRequestSnafu)?,
|
||||||
|
),
|
||||||
|
case_sensitive: c.case_sensitive,
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let request = AlterTableRequest {
|
let request = AlterTableRequest {
|
||||||
|
|||||||
@@ -125,6 +125,14 @@ pub enum Error {
|
|||||||
#[snafu(source)]
|
#[snafu(source)]
|
||||||
error: MetadataError,
|
error: MetadataError,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Invalid change fulltext option request"))]
|
||||||
|
InvalidChangeFulltextOptionRequest {
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
#[snafu(source)]
|
||||||
|
error: prost::DecodeError,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
@@ -148,7 +156,8 @@ impl ErrorExt for Error {
|
|||||||
Error::UnknownColumnDataType { .. } | Error::InvalidFulltextColumnType { .. } => {
|
Error::UnknownColumnDataType { .. } | Error::InvalidFulltextColumnType { .. } => {
|
||||||
StatusCode::InvalidArguments
|
StatusCode::InvalidArguments
|
||||||
}
|
}
|
||||||
Error::InvalidChangeTableOptionRequest { .. } => StatusCode::InvalidArguments,
|
Error::InvalidChangeTableOptionRequest { .. }
|
||||||
|
| Error::InvalidChangeFulltextOptionRequest { .. } => StatusCode::InvalidArguments,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,11 @@
|
|||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use api::v1::column_data_type_extension::TypeExt;
|
||||||
use api::v1::column_def::contains_fulltext;
|
use api::v1::column_def::contains_fulltext;
|
||||||
use api::v1::{
|
use api::v1::{
|
||||||
AddColumn, AddColumns, Column, ColumnDataType, ColumnDataTypeExtension, ColumnDef,
|
AddColumn, AddColumns, Column, ColumnDataType, ColumnDataTypeExtension, ColumnDef,
|
||||||
ColumnOptions, ColumnSchema, CreateTableExpr, SemanticType,
|
ColumnOptions, ColumnSchema, CreateTableExpr, JsonTypeExtension, SemanticType,
|
||||||
};
|
};
|
||||||
use datatypes::schema::Schema;
|
use datatypes::schema::Schema;
|
||||||
use snafu::{ensure, OptionExt, ResultExt};
|
use snafu::{ensure, OptionExt, ResultExt};
|
||||||
@@ -25,8 +26,9 @@ use table::metadata::TableId;
|
|||||||
use table::table_reference::TableReference;
|
use table::table_reference::TableReference;
|
||||||
|
|
||||||
use crate::error::{
|
use crate::error::{
|
||||||
DuplicatedColumnNameSnafu, DuplicatedTimestampColumnSnafu, InvalidFulltextColumnTypeSnafu,
|
self, DuplicatedColumnNameSnafu, DuplicatedTimestampColumnSnafu,
|
||||||
MissingTimestampColumnSnafu, Result, UnknownColumnDataTypeSnafu,
|
InvalidFulltextColumnTypeSnafu, MissingTimestampColumnSnafu, Result,
|
||||||
|
UnknownColumnDataTypeSnafu,
|
||||||
};
|
};
|
||||||
pub struct ColumnExpr<'a> {
|
pub struct ColumnExpr<'a> {
|
||||||
pub column_name: &'a str,
|
pub column_name: &'a str,
|
||||||
@@ -72,6 +74,28 @@ impl<'a> From<&'a ColumnSchema> for ColumnExpr<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn infer_column_datatype(
|
||||||
|
datatype: i32,
|
||||||
|
datatype_extension: &Option<ColumnDataTypeExtension>,
|
||||||
|
) -> Result<ColumnDataType> {
|
||||||
|
let column_type =
|
||||||
|
ColumnDataType::try_from(datatype).context(UnknownColumnDataTypeSnafu { datatype })?;
|
||||||
|
|
||||||
|
if matches!(&column_type, ColumnDataType::Binary) {
|
||||||
|
if let Some(ext) = datatype_extension {
|
||||||
|
let type_ext = ext
|
||||||
|
.type_ext
|
||||||
|
.as_ref()
|
||||||
|
.context(error::MissingFieldSnafu { field: "type_ext" })?;
|
||||||
|
if *type_ext == TypeExt::JsonType(JsonTypeExtension::JsonBinary.into()) {
|
||||||
|
return Ok(ColumnDataType::Json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(column_type)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build_create_table_expr(
|
pub fn build_create_table_expr(
|
||||||
table_id: Option<TableId>,
|
table_id: Option<TableId>,
|
||||||
table_name: &TableReference<'_>,
|
table_name: &TableReference<'_>,
|
||||||
@@ -124,8 +148,7 @@ pub fn build_create_table_expr(
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let column_type =
|
let column_type = infer_column_datatype(datatype, datatype_extension)?;
|
||||||
ColumnDataType::try_from(datatype).context(UnknownColumnDataTypeSnafu { datatype })?;
|
|
||||||
|
|
||||||
ensure!(
|
ensure!(
|
||||||
!contains_fulltext(options) || column_type == ColumnDataType::String,
|
!contains_fulltext(options) || column_type == ColumnDataType::String,
|
||||||
|
|||||||
@@ -218,6 +218,12 @@ pub fn values(arrays: &[VectorRef]) -> Result<Values> {
|
|||||||
Decimal128Vector,
|
Decimal128Vector,
|
||||||
decimal128_values,
|
decimal128_values,
|
||||||
|x| { convert_to_pb_decimal128(x) }
|
|x| { convert_to_pb_decimal128(x) }
|
||||||
|
),
|
||||||
|
(
|
||||||
|
ConcreteDataType::Vector(_),
|
||||||
|
BinaryVector,
|
||||||
|
binary_values,
|
||||||
|
|x| { x.into() }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ derive_builder.workspace = true
|
|||||||
etcd-client.workspace = true
|
etcd-client.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
futures-util.workspace = true
|
futures-util.workspace = true
|
||||||
hex = { version = "0.4" }
|
hex.workspace = true
|
||||||
humantime-serde.workspace = true
|
humantime-serde.workspace = true
|
||||||
itertools.workspace = true
|
itertools.workspace = true
|
||||||
lazy_static.workspace = true
|
lazy_static.workspace = true
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ pub struct RegionStat {
|
|||||||
pub rcus: i64,
|
pub rcus: i64,
|
||||||
/// The write capacity units during this period
|
/// The write capacity units during this period
|
||||||
pub wcus: i64,
|
pub wcus: i64,
|
||||||
/// Approximate bytes of this region
|
/// Approximate disk bytes of this region, including sst, index, manifest and wal
|
||||||
pub approximate_bytes: u64,
|
pub approximate_bytes: u64,
|
||||||
/// The engine name.
|
/// The engine name.
|
||||||
pub engine: String,
|
pub engine: String,
|
||||||
|
|||||||
@@ -107,6 +107,9 @@ fn create_proto_alter_kind(
|
|||||||
}
|
}
|
||||||
Kind::RenameTable(_) => Ok(None),
|
Kind::RenameTable(_) => Ok(None),
|
||||||
Kind::ChangeTableOptions(v) => Ok(Some(alter_request::Kind::ChangeTableOptions(v.clone()))),
|
Kind::ChangeTableOptions(v) => Ok(Some(alter_request::Kind::ChangeTableOptions(v.clone()))),
|
||||||
|
Kind::ChangeColumnFulltext(v) => {
|
||||||
|
Ok(Some(alter_request::Kind::ChangeColumnFulltext(v.clone())))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ impl AlterTableProcedure {
|
|||||||
}
|
}
|
||||||
AlterKind::DropColumns { .. }
|
AlterKind::DropColumns { .. }
|
||||||
| AlterKind::ChangeColumnTypes { .. }
|
| AlterKind::ChangeColumnTypes { .. }
|
||||||
| AlterKind::ChangeTableOptions { .. } => {}
|
| AlterKind::ChangeTableOptions { .. }
|
||||||
|
| AlterKind::ChangeColumnFulltext { .. } => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(new_info)
|
Ok(new_info)
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ pub mod catalog_name;
|
|||||||
pub mod datanode_table;
|
pub mod datanode_table;
|
||||||
pub mod flow;
|
pub mod flow;
|
||||||
pub mod node_address;
|
pub mod node_address;
|
||||||
|
mod schema_metadata_manager;
|
||||||
pub mod schema_name;
|
pub mod schema_name;
|
||||||
pub mod table_info;
|
pub mod table_info;
|
||||||
pub mod table_name;
|
pub mod table_name;
|
||||||
@@ -116,6 +117,7 @@ use flow::flow_route::FlowRouteValue;
|
|||||||
use flow::table_flow::TableFlowValue;
|
use flow::table_flow::TableFlowValue;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
pub use schema_metadata_manager::{SchemaMetadataManager, SchemaMetadataManagerRef};
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use snafu::{ensure, OptionExt, ResultExt};
|
use snafu::{ensure, OptionExt, ResultExt};
|
||||||
|
|||||||
122
src/common/meta/src/key/schema_metadata_manager.rs
Normal file
122
src/common/meta/src/key/schema_metadata_manager.rs
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
// Copyright 2023 Greptime Team
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//! Schema-level metadata manager.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use snafu::OptionExt;
|
||||||
|
use store_api::storage::TableId;
|
||||||
|
|
||||||
|
use crate::error::TableInfoNotFoundSnafu;
|
||||||
|
use crate::key::schema_name::{SchemaManager, SchemaNameKey};
|
||||||
|
use crate::key::table_info::{TableInfoManager, TableInfoManagerRef};
|
||||||
|
use crate::kv_backend::KvBackendRef;
|
||||||
|
use crate::{error, SchemaOptions};
|
||||||
|
|
||||||
|
pub type SchemaMetadataManagerRef = Arc<SchemaMetadataManager>;
|
||||||
|
|
||||||
|
pub struct SchemaMetadataManager {
|
||||||
|
table_info_manager: TableInfoManagerRef,
|
||||||
|
schema_manager: SchemaManager,
|
||||||
|
#[cfg(any(test, feature = "testing"))]
|
||||||
|
kv_backend: KvBackendRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SchemaMetadataManager {
|
||||||
|
/// Creates a new database meta
|
||||||
|
#[cfg(not(any(test, feature = "testing")))]
|
||||||
|
pub fn new(kv_backend: KvBackendRef) -> Self {
|
||||||
|
let table_info_manager = Arc::new(TableInfoManager::new(kv_backend.clone()));
|
||||||
|
let schema_manager = SchemaManager::new(kv_backend);
|
||||||
|
Self {
|
||||||
|
table_info_manager,
|
||||||
|
schema_manager,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new database meta
|
||||||
|
#[cfg(any(test, feature = "testing"))]
|
||||||
|
pub fn new(kv_backend: KvBackendRef) -> Self {
|
||||||
|
let table_info_manager = Arc::new(TableInfoManager::new(kv_backend.clone()));
|
||||||
|
let schema_manager = SchemaManager::new(kv_backend.clone());
|
||||||
|
Self {
|
||||||
|
table_info_manager,
|
||||||
|
schema_manager,
|
||||||
|
kv_backend,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets schema options by table id.
|
||||||
|
pub async fn get_schema_options_by_table_id(
|
||||||
|
&self,
|
||||||
|
table_id: TableId,
|
||||||
|
) -> error::Result<Option<SchemaOptions>> {
|
||||||
|
let table_info = self
|
||||||
|
.table_info_manager
|
||||||
|
.get(table_id)
|
||||||
|
.await?
|
||||||
|
.with_context(|| TableInfoNotFoundSnafu {
|
||||||
|
table: format!("table id: {}", table_id),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let key = SchemaNameKey::new(
|
||||||
|
&table_info.table_info.catalog_name,
|
||||||
|
&table_info.table_info.schema_name,
|
||||||
|
);
|
||||||
|
self.schema_manager.get(key).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "testing"))]
|
||||||
|
pub async fn register_region_table_info(
|
||||||
|
&self,
|
||||||
|
table_id: TableId,
|
||||||
|
table_name: &str,
|
||||||
|
schema_name: &str,
|
||||||
|
catalog_name: &str,
|
||||||
|
schema_value: Option<crate::key::schema_name::SchemaNameValue>,
|
||||||
|
) {
|
||||||
|
use table::metadata::{RawTableInfo, TableType};
|
||||||
|
let value = crate::key::table_info::TableInfoValue::new(RawTableInfo {
|
||||||
|
ident: Default::default(),
|
||||||
|
name: table_name.to_string(),
|
||||||
|
desc: None,
|
||||||
|
catalog_name: catalog_name.to_string(),
|
||||||
|
schema_name: schema_name.to_string(),
|
||||||
|
meta: Default::default(),
|
||||||
|
table_type: TableType::Base,
|
||||||
|
});
|
||||||
|
let (txn, _) = self
|
||||||
|
.table_info_manager
|
||||||
|
.build_create_txn(table_id, &value)
|
||||||
|
.unwrap();
|
||||||
|
let resp = self.kv_backend.txn(txn).await.unwrap();
|
||||||
|
assert!(resp.succeeded, "Failed to create table metadata");
|
||||||
|
let key = SchemaNameKey {
|
||||||
|
catalog: catalog_name,
|
||||||
|
schema: schema_name,
|
||||||
|
};
|
||||||
|
self.schema_manager
|
||||||
|
.create(key, schema_value, false)
|
||||||
|
.await
|
||||||
|
.expect("Failed to create schema metadata");
|
||||||
|
common_telemetry::info!(
|
||||||
|
"Register table: {}, id: {}, schema: {}, catalog: {}",
|
||||||
|
table_name,
|
||||||
|
table_id,
|
||||||
|
schema_name,
|
||||||
|
catalog_name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -134,6 +134,7 @@ impl TableInfoValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub type TableInfoManagerRef = Arc<TableInfoManager>;
|
pub type TableInfoManagerRef = Arc<TableInfoManager>;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TableInfoManager {
|
pub struct TableInfoManager {
|
||||||
kv_backend: KvBackendRef,
|
kv_backend: KvBackendRef,
|
||||||
|
|||||||
@@ -54,4 +54,7 @@ pub type DatanodeId = u64;
|
|||||||
// The id of the flownode.
|
// The id of the flownode.
|
||||||
pub type FlownodeId = u64;
|
pub type FlownodeId = u64;
|
||||||
|
|
||||||
|
/// Schema options.
|
||||||
|
pub type SchemaOptions = key::schema_name::SchemaNameValue;
|
||||||
|
|
||||||
pub use instruction::RegionIdent;
|
pub use instruction::RegionIdent;
|
||||||
|
|||||||
13
src/common/options/Cargo.toml
Normal file
13
src/common/options/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "common-options"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
common-grpc.workspace = true
|
||||||
|
humantime-serde.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
@@ -18,20 +18,23 @@ use common_grpc::channel_manager;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||||
pub struct DatanodeOptions {
|
pub struct DatanodeClientOptions {
|
||||||
pub client: DatanodeClientOptions,
|
pub client: ClientOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct DatanodeClientOptions {
|
pub struct ClientOptions {
|
||||||
|
#[serde(with = "humantime_serde")]
|
||||||
|
pub timeout: Duration,
|
||||||
#[serde(with = "humantime_serde")]
|
#[serde(with = "humantime_serde")]
|
||||||
pub connect_timeout: Duration,
|
pub connect_timeout: Duration,
|
||||||
pub tcp_nodelay: bool,
|
pub tcp_nodelay: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for DatanodeClientOptions {
|
impl Default for ClientOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
timeout: Duration::from_secs(channel_manager::DEFAULT_GRPC_REQUEST_TIMEOUT_SECS),
|
||||||
connect_timeout: Duration::from_secs(
|
connect_timeout: Duration::from_secs(
|
||||||
channel_manager::DEFAULT_GRPC_CONNECT_TIMEOUT_SECS,
|
channel_manager::DEFAULT_GRPC_CONNECT_TIMEOUT_SECS,
|
||||||
),
|
),
|
||||||
@@ -12,17 +12,4 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::sync::Arc;
|
pub mod datanode;
|
||||||
|
|
||||||
use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest;
|
|
||||||
|
|
||||||
use super::trace::span::TraceSpans;
|
|
||||||
|
|
||||||
/// Transformer helps to transform ExportTraceServiceRequest based on logic, like:
|
|
||||||
/// - uplift some fields from Attributes (Map type) to column
|
|
||||||
pub trait TraceParser: Send + Sync {
|
|
||||||
fn parse(&self, request: ExportTraceServiceRequest) -> TraceSpans;
|
|
||||||
fn table_name(&self) -> String;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type TraceParserRef = Arc<dyn TraceParser>;
|
|
||||||
@@ -427,7 +427,8 @@ mod test {
|
|||||||
common_telemetry::init_default_ut_logging();
|
common_telemetry::init_default_ut_logging();
|
||||||
let mut region_server = mock_region_server();
|
let mut region_server = mock_region_server();
|
||||||
let mut engine_env = TestEnv::with_prefix("region-alive-keeper");
|
let mut engine_env = TestEnv::with_prefix("region-alive-keeper");
|
||||||
let engine = Arc::new(engine_env.create_engine(MitoConfig::default()).await);
|
let engine = engine_env.create_engine(MitoConfig::default()).await;
|
||||||
|
let engine = Arc::new(engine);
|
||||||
region_server.register_engine(engine.clone());
|
region_server.register_engine(engine.clone());
|
||||||
|
|
||||||
let alive_keeper = Arc::new(RegionAliveKeeper::new(region_server.clone(), 100));
|
let alive_keeper = Arc::new(RegionAliveKeeper::new(region_server.clone(), 100));
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ use servers::heartbeat_options::HeartbeatOptions;
|
|||||||
use servers::http::HttpOptions;
|
use servers::http::HttpOptions;
|
||||||
use servers::Mode;
|
use servers::Mode;
|
||||||
|
|
||||||
pub const DEFAULT_OBJECT_STORE_CACHE_SIZE: ReadableSize = ReadableSize::mb(256);
|
pub const DEFAULT_OBJECT_STORE_CACHE_SIZE: ReadableSize = ReadableSize::gb(1);
|
||||||
|
|
||||||
/// Default data home in file storage
|
/// Default data home in file storage
|
||||||
const DEFAULT_DATA_HOME: &str = "/tmp/greptimedb";
|
const DEFAULT_DATA_HOME: &str = "/tmp/greptimedb";
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ use common_base::Plugins;
|
|||||||
use common_error::ext::BoxedError;
|
use common_error::ext::BoxedError;
|
||||||
use common_greptimedb_telemetry::GreptimeDBTelemetryTask;
|
use common_greptimedb_telemetry::GreptimeDBTelemetryTask;
|
||||||
use common_meta::key::datanode_table::{DatanodeTableManager, DatanodeTableValue};
|
use common_meta::key::datanode_table::{DatanodeTableManager, DatanodeTableValue};
|
||||||
|
use common_meta::key::{SchemaMetadataManager, SchemaMetadataManagerRef};
|
||||||
use common_meta::kv_backend::KvBackendRef;
|
use common_meta::kv_backend::KvBackendRef;
|
||||||
use common_meta::wal_options_allocator::prepare_wal_options;
|
use common_meta::wal_options_allocator::prepare_wal_options;
|
||||||
pub use common_procedure::options::ProcedureConfig;
|
pub use common_procedure::options::ProcedureConfig;
|
||||||
@@ -207,7 +208,10 @@ impl DatanodeBuilder {
|
|||||||
(Box::new(NoopRegionServerEventListener) as _, None)
|
(Box::new(NoopRegionServerEventListener) as _, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
let region_server = self.new_region_server(region_event_listener).await?;
|
let schema_metadata_manager = Arc::new(SchemaMetadataManager::new(kv_backend.clone()));
|
||||||
|
let region_server = self
|
||||||
|
.new_region_server(schema_metadata_manager, region_event_listener)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let datanode_table_manager = DatanodeTableManager::new(kv_backend.clone());
|
let datanode_table_manager = DatanodeTableManager::new(kv_backend.clone());
|
||||||
let table_values = datanode_table_manager
|
let table_values = datanode_table_manager
|
||||||
@@ -312,6 +316,7 @@ impl DatanodeBuilder {
|
|||||||
|
|
||||||
async fn new_region_server(
|
async fn new_region_server(
|
||||||
&self,
|
&self,
|
||||||
|
schema_metadata_manager: SchemaMetadataManagerRef,
|
||||||
event_listener: RegionServerEventListenerRef,
|
event_listener: RegionServerEventListenerRef,
|
||||||
) -> Result<RegionServer> {
|
) -> Result<RegionServer> {
|
||||||
let opts: &DatanodeOptions = &self.opts;
|
let opts: &DatanodeOptions = &self.opts;
|
||||||
@@ -340,8 +345,13 @@ impl DatanodeBuilder {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let object_store_manager = Self::build_object_store_manager(&opts.storage).await?;
|
let object_store_manager = Self::build_object_store_manager(&opts.storage).await?;
|
||||||
let engines =
|
let engines = Self::build_store_engines(
|
||||||
Self::build_store_engines(opts, object_store_manager, self.plugins.clone()).await?;
|
opts,
|
||||||
|
object_store_manager,
|
||||||
|
schema_metadata_manager,
|
||||||
|
self.plugins.clone(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
for engine in engines {
|
for engine in engines {
|
||||||
region_server.register_engine(engine);
|
region_server.register_engine(engine);
|
||||||
}
|
}
|
||||||
@@ -355,6 +365,7 @@ impl DatanodeBuilder {
|
|||||||
async fn build_store_engines(
|
async fn build_store_engines(
|
||||||
opts: &DatanodeOptions,
|
opts: &DatanodeOptions,
|
||||||
object_store_manager: ObjectStoreManagerRef,
|
object_store_manager: ObjectStoreManagerRef,
|
||||||
|
schema_metadata_manager: SchemaMetadataManagerRef,
|
||||||
plugins: Plugins,
|
plugins: Plugins,
|
||||||
) -> Result<Vec<RegionEngineRef>> {
|
) -> Result<Vec<RegionEngineRef>> {
|
||||||
let mut engines = vec![];
|
let mut engines = vec![];
|
||||||
@@ -365,6 +376,7 @@ impl DatanodeBuilder {
|
|||||||
opts,
|
opts,
|
||||||
object_store_manager.clone(),
|
object_store_manager.clone(),
|
||||||
config.clone(),
|
config.clone(),
|
||||||
|
schema_metadata_manager.clone(),
|
||||||
plugins.clone(),
|
plugins.clone(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -390,6 +402,7 @@ impl DatanodeBuilder {
|
|||||||
opts: &DatanodeOptions,
|
opts: &DatanodeOptions,
|
||||||
object_store_manager: ObjectStoreManagerRef,
|
object_store_manager: ObjectStoreManagerRef,
|
||||||
config: MitoConfig,
|
config: MitoConfig,
|
||||||
|
schema_metadata_manager: SchemaMetadataManagerRef,
|
||||||
plugins: Plugins,
|
plugins: Plugins,
|
||||||
) -> Result<MitoEngine> {
|
) -> Result<MitoEngine> {
|
||||||
let mito_engine = match &opts.wal {
|
let mito_engine = match &opts.wal {
|
||||||
@@ -399,6 +412,7 @@ impl DatanodeBuilder {
|
|||||||
Self::build_raft_engine_log_store(&opts.storage.data_home, raft_engine_config)
|
Self::build_raft_engine_log_store(&opts.storage.data_home, raft_engine_config)
|
||||||
.await?,
|
.await?,
|
||||||
object_store_manager,
|
object_store_manager,
|
||||||
|
schema_metadata_manager,
|
||||||
plugins,
|
plugins,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@@ -429,6 +443,7 @@ impl DatanodeBuilder {
|
|||||||
config,
|
config,
|
||||||
Self::build_kafka_log_store(kafka_config, global_index_collector).await?,
|
Self::build_kafka_log_store(kafka_config, global_index_collector).await?,
|
||||||
object_store_manager,
|
object_store_manager,
|
||||||
|
schema_metadata_manager,
|
||||||
plugins,
|
plugins,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -1355,7 +1355,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_region_server_parallism() {
|
async fn test_region_server_parallelism() {
|
||||||
let p = RegionServerParallelism::from_opts(2, Duration::from_millis(1)).unwrap();
|
let p = RegionServerParallelism::from_opts(2, Duration::from_millis(1)).unwrap();
|
||||||
let first_query = p.acquire().await;
|
let first_query = p.acquire().await;
|
||||||
assert!(first_query.is_ok());
|
assert!(first_query.is_ok());
|
||||||
|
|||||||
@@ -33,3 +33,5 @@ paste = "1.0"
|
|||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
snafu.workspace = true
|
snafu.workspace = true
|
||||||
|
sqlparser.workspace = true
|
||||||
|
sqlparser_derive = "0.1"
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ use crate::types::{
|
|||||||
IntervalDayTimeType, IntervalMonthDayNanoType, IntervalType, IntervalYearMonthType, JsonType,
|
IntervalDayTimeType, IntervalMonthDayNanoType, IntervalType, IntervalYearMonthType, JsonType,
|
||||||
ListType, NullType, StringType, TimeMillisecondType, TimeType, TimestampMicrosecondType,
|
ListType, NullType, StringType, TimeMillisecondType, TimeType, TimestampMicrosecondType,
|
||||||
TimestampMillisecondType, TimestampNanosecondType, TimestampSecondType, TimestampType,
|
TimestampMillisecondType, TimestampNanosecondType, TimestampSecondType, TimestampType,
|
||||||
UInt16Type, UInt32Type, UInt64Type, UInt8Type,
|
UInt16Type, UInt32Type, UInt64Type, UInt8Type, VectorType,
|
||||||
};
|
};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::vectors::MutableVector;
|
use crate::vectors::MutableVector;
|
||||||
@@ -84,6 +84,9 @@ pub enum ConcreteDataType {
|
|||||||
|
|
||||||
// JSON type:
|
// JSON type:
|
||||||
Json(JsonType),
|
Json(JsonType),
|
||||||
|
|
||||||
|
// Vector type:
|
||||||
|
Vector(VectorType),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ConcreteDataType {
|
impl fmt::Display for ConcreteDataType {
|
||||||
@@ -132,6 +135,7 @@ impl fmt::Display for ConcreteDataType {
|
|||||||
ConcreteDataType::List(v) => write!(f, "{}", v.name()),
|
ConcreteDataType::List(v) => write!(f, "{}", v.name()),
|
||||||
ConcreteDataType::Dictionary(v) => write!(f, "{}", v.name()),
|
ConcreteDataType::Dictionary(v) => write!(f, "{}", v.name()),
|
||||||
ConcreteDataType::Json(v) => write!(f, "{}", v.name()),
|
ConcreteDataType::Json(v) => write!(f, "{}", v.name()),
|
||||||
|
ConcreteDataType::Vector(v) => write!(f, "{}", v.name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,6 +171,7 @@ impl ConcreteDataType {
|
|||||||
| ConcreteDataType::Decimal128(_)
|
| ConcreteDataType::Decimal128(_)
|
||||||
| ConcreteDataType::Binary(_)
|
| ConcreteDataType::Binary(_)
|
||||||
| ConcreteDataType::Json(_)
|
| ConcreteDataType::Json(_)
|
||||||
|
| ConcreteDataType::Vector(_)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,6 +230,10 @@ impl ConcreteDataType {
|
|||||||
matches!(self, ConcreteDataType::Json(_))
|
matches!(self, ConcreteDataType::Json(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_vector(&self) -> bool {
|
||||||
|
matches!(self, ConcreteDataType::Vector(_))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn numerics() -> Vec<ConcreteDataType> {
|
pub fn numerics() -> Vec<ConcreteDataType> {
|
||||||
vec![
|
vec![
|
||||||
ConcreteDataType::int8_datatype(),
|
ConcreteDataType::int8_datatype(),
|
||||||
@@ -334,6 +343,20 @@ impl ConcreteDataType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_json(&self) -> Option<JsonType> {
|
||||||
|
match self {
|
||||||
|
ConcreteDataType::Json(j) => Some(*j),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_vector(&self) -> Option<VectorType> {
|
||||||
|
match self {
|
||||||
|
ConcreteDataType::Vector(v) => Some(*v),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks if the data type can cast to another data type.
|
/// Checks if the data type can cast to another data type.
|
||||||
pub fn can_arrow_type_cast_to(&self, to_type: &ConcreteDataType) -> bool {
|
pub fn can_arrow_type_cast_to(&self, to_type: &ConcreteDataType) -> bool {
|
||||||
let array = arrow_array::new_empty_array(&self.as_arrow_type());
|
let array = arrow_array::new_empty_array(&self.as_arrow_type());
|
||||||
@@ -564,6 +587,14 @@ impl ConcreteDataType {
|
|||||||
pub fn decimal128_default_datatype() -> ConcreteDataType {
|
pub fn decimal128_default_datatype() -> ConcreteDataType {
|
||||||
Self::decimal128_datatype(DECIMAL128_MAX_PRECISION, DECIMAL_DEFAULT_SCALE)
|
Self::decimal128_datatype(DECIMAL128_MAX_PRECISION, DECIMAL_DEFAULT_SCALE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn vector_datatype(dim: u32) -> ConcreteDataType {
|
||||||
|
ConcreteDataType::Vector(VectorType::new(dim))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vector_default_datatype() -> ConcreteDataType {
|
||||||
|
Self::vector_datatype(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data type abstraction.
|
/// Data type abstraction.
|
||||||
@@ -757,6 +788,7 @@ mod tests {
|
|||||||
assert!(ConcreteDataType::duration_microsecond_datatype().is_stringifiable());
|
assert!(ConcreteDataType::duration_microsecond_datatype().is_stringifiable());
|
||||||
assert!(ConcreteDataType::duration_nanosecond_datatype().is_stringifiable());
|
assert!(ConcreteDataType::duration_nanosecond_datatype().is_stringifiable());
|
||||||
assert!(ConcreteDataType::decimal128_datatype(10, 2).is_stringifiable());
|
assert!(ConcreteDataType::decimal128_datatype(10, 2).is_stringifiable());
|
||||||
|
assert!(ConcreteDataType::vector_default_datatype().is_stringifiable());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -909,5 +941,9 @@ mod tests {
|
|||||||
.to_string(),
|
.to_string(),
|
||||||
"Dictionary<Int32, String>"
|
"Dictionary<Int32, String>"
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ConcreteDataType::vector_datatype(3).to_string(),
|
||||||
|
"Vector(3)"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,6 +196,13 @@ pub enum Error {
|
|||||||
location: Location,
|
location: Location,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Invalid Vector: {}", msg))]
|
||||||
|
InvalidVector {
|
||||||
|
msg: String,
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
},
|
||||||
|
|
||||||
#[snafu(display("Value exceeds the precision {} bound", precision))]
|
#[snafu(display("Value exceeds the precision {} bound", precision))]
|
||||||
ValueExceedsPrecision {
|
ValueExceedsPrecision {
|
||||||
precision: u8,
|
precision: u8,
|
||||||
@@ -212,6 +219,19 @@ pub enum Error {
|
|||||||
#[snafu(implicit)]
|
#[snafu(implicit)]
|
||||||
location: Location,
|
location: Location,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Failed to parse extended type in metadata: {}", value))]
|
||||||
|
ParseExtendedType {
|
||||||
|
value: String,
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
},
|
||||||
|
#[snafu(display("Invalid fulltext option: {}", msg))]
|
||||||
|
InvalidFulltextOption {
|
||||||
|
msg: String,
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ErrorExt for Error {
|
impl ErrorExt for Error {
|
||||||
@@ -230,7 +250,9 @@ impl ErrorExt for Error {
|
|||||||
| DuplicateMeta { .. }
|
| DuplicateMeta { .. }
|
||||||
| InvalidTimestampPrecision { .. }
|
| InvalidTimestampPrecision { .. }
|
||||||
| InvalidPrecisionOrScale { .. }
|
| InvalidPrecisionOrScale { .. }
|
||||||
| InvalidJson { .. } => StatusCode::InvalidArguments,
|
| InvalidJson { .. }
|
||||||
|
| InvalidVector { .. }
|
||||||
|
| InvalidFulltextOption { .. } => StatusCode::InvalidArguments,
|
||||||
|
|
||||||
ValueExceedsPrecision { .. }
|
ValueExceedsPrecision { .. }
|
||||||
| CastType { .. }
|
| CastType { .. }
|
||||||
@@ -245,7 +267,8 @@ impl ErrorExt for Error {
|
|||||||
| ProjectArrowSchema { .. }
|
| ProjectArrowSchema { .. }
|
||||||
| ToScalarValue { .. }
|
| ToScalarValue { .. }
|
||||||
| TryFromValue { .. }
|
| TryFromValue { .. }
|
||||||
| ConvertArrowArrayToScalars { .. } => StatusCode::Internal,
|
| ConvertArrowArrayToScalars { .. }
|
||||||
|
| ParseExtendedType { .. } => StatusCode::Internal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,13 +21,16 @@ use std::fmt;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use arrow::datatypes::{Field, Schema as ArrowSchema};
|
use arrow::datatypes::{Field, Schema as ArrowSchema};
|
||||||
|
use column_schema::ColumnExtType;
|
||||||
use datafusion_common::DFSchemaRef;
|
use datafusion_common::DFSchemaRef;
|
||||||
use snafu::{ensure, ResultExt};
|
use snafu::{ensure, ResultExt};
|
||||||
|
|
||||||
use crate::error::{self, DuplicateColumnSnafu, Error, ProjectArrowSchemaSnafu, Result};
|
use crate::error::{self, DuplicateColumnSnafu, Error, ProjectArrowSchemaSnafu, Result};
|
||||||
use crate::prelude::DataType;
|
use crate::prelude::ConcreteDataType;
|
||||||
pub use crate::schema::column_schema::{
|
pub use crate::schema::column_schema::{
|
||||||
ColumnSchema, FulltextAnalyzer, FulltextOptions, Metadata, COMMENT_KEY, FULLTEXT_KEY,
|
ColumnSchema, FulltextAnalyzer, FulltextOptions, Metadata,
|
||||||
|
COLUMN_FULLTEXT_CHANGE_OPT_KEY_ENABLE, COLUMN_FULLTEXT_OPT_KEY_ANALYZER,
|
||||||
|
COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE, COMMENT_KEY, FULLTEXT_KEY, INVERTED_INDEX_KEY,
|
||||||
TIME_INDEX_KEY,
|
TIME_INDEX_KEY,
|
||||||
};
|
};
|
||||||
pub use crate::schema::constraint::ColumnDefaultConstraint;
|
pub use crate::schema::constraint::ColumnDefaultConstraint;
|
||||||
@@ -261,9 +264,14 @@ fn collect_fields(column_schemas: &[ColumnSchema]) -> Result<FieldsAndIndices> {
|
|||||||
}
|
}
|
||||||
let mut field = Field::try_from(column_schema)?;
|
let mut field = Field::try_from(column_schema)?;
|
||||||
|
|
||||||
// Json column performs the same as binary column in Arrow, so we need to mark it
|
// Column with type Json or Vector performs the same as binary column in Arrow, so we need to mark it
|
||||||
if column_schema.data_type.is_json() {
|
let extype = match column_schema.data_type {
|
||||||
let metadata = HashMap::from([(TYPE_KEY.to_string(), column_schema.data_type.name())]);
|
ConcreteDataType::Json(_) => Some(ColumnExtType::Json),
|
||||||
|
ConcreteDataType::Vector(d) => Some(ColumnExtType::Vector(d.dim)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
if let Some(extype) = extype {
|
||||||
|
let metadata = HashMap::from([(TYPE_KEY.to_string(), extype.to_string())]);
|
||||||
field = field.with_metadata(metadata);
|
field = field.with_metadata(metadata);
|
||||||
}
|
}
|
||||||
fields.push(field);
|
fields.push(field);
|
||||||
|
|||||||
@@ -14,16 +14,17 @@
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use arrow::datatypes::Field;
|
use arrow::datatypes::Field;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use snafu::{ensure, ResultExt};
|
use snafu::{ensure, ResultExt};
|
||||||
|
use sqlparser_derive::{Visit, VisitMut};
|
||||||
|
|
||||||
use crate::data_type::{ConcreteDataType, DataType};
|
use crate::data_type::{ConcreteDataType, DataType};
|
||||||
use crate::error::{self, Error, Result};
|
use crate::error::{self, Error, InvalidFulltextOptionSnafu, ParseExtendedTypeSnafu, Result};
|
||||||
use crate::schema::constraint::ColumnDefaultConstraint;
|
use crate::schema::constraint::ColumnDefaultConstraint;
|
||||||
use crate::schema::TYPE_KEY;
|
use crate::schema::TYPE_KEY;
|
||||||
use crate::types::JSON_TYPE_NAME;
|
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::vectors::VectorRef;
|
use crate::vectors::VectorRef;
|
||||||
|
|
||||||
@@ -36,6 +37,13 @@ pub const COMMENT_KEY: &str = "greptime:storage:comment";
|
|||||||
const DEFAULT_CONSTRAINT_KEY: &str = "greptime:default_constraint";
|
const DEFAULT_CONSTRAINT_KEY: &str = "greptime:default_constraint";
|
||||||
/// Key used to store fulltext options in arrow field's metadata.
|
/// Key used to store fulltext options in arrow field's metadata.
|
||||||
pub const FULLTEXT_KEY: &str = "greptime:fulltext";
|
pub const FULLTEXT_KEY: &str = "greptime:fulltext";
|
||||||
|
/// Key used to store whether the column has inverted index in arrow field's metadata.
|
||||||
|
pub const INVERTED_INDEX_KEY: &str = "greptime:inverted_index";
|
||||||
|
|
||||||
|
/// Keys used in fulltext options
|
||||||
|
pub const COLUMN_FULLTEXT_CHANGE_OPT_KEY_ENABLE: &str = "enable";
|
||||||
|
pub const COLUMN_FULLTEXT_OPT_KEY_ANALYZER: &str = "analyzer";
|
||||||
|
pub const COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE: &str = "case_sensitive";
|
||||||
|
|
||||||
/// Schema of a column, used as an immutable struct.
|
/// Schema of a column, used as an immutable struct.
|
||||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
@@ -134,6 +142,24 @@ impl ColumnSchema {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_inverted_index(mut self, value: bool) -> Self {
|
||||||
|
let _ = self
|
||||||
|
.metadata
|
||||||
|
.insert(INVERTED_INDEX_KEY.to_string(), value.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_inverted_indexed(&self) -> bool {
|
||||||
|
self.metadata
|
||||||
|
.get(INVERTED_INDEX_KEY)
|
||||||
|
.map(|v| v.eq_ignore_ascii_case("true"))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_inverted_index_key(&self) -> bool {
|
||||||
|
self.metadata.contains_key(INVERTED_INDEX_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
/// Set default constraint.
|
/// Set default constraint.
|
||||||
///
|
///
|
||||||
/// If a default constraint exists for the column, this method will
|
/// If a default constraint exists for the column, this method will
|
||||||
@@ -264,6 +290,48 @@ impl ColumnSchema {
|
|||||||
);
|
);
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_fulltext_options(&mut self, options: &FulltextOptions) -> Result<()> {
|
||||||
|
self.metadata.insert(
|
||||||
|
FULLTEXT_KEY.to_string(),
|
||||||
|
serde_json::to_string(options).context(error::SerializeSnafu)?,
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Column extended type set in column schema's metadata.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ColumnExtType {
|
||||||
|
/// Json type.
|
||||||
|
Json,
|
||||||
|
|
||||||
|
/// Vector type with dimension.
|
||||||
|
Vector(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ColumnExtType {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ColumnExtType::Json => write!(f, "Json"),
|
||||||
|
ColumnExtType::Vector(dim) => write!(f, "Vector({})", dim),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ColumnExtType {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"Json" => Ok(ColumnExtType::Json),
|
||||||
|
_ if s.starts_with("Vector(") && s.ends_with(')') => s[7..s.len() - 1]
|
||||||
|
.parse::<u32>()
|
||||||
|
.map(ColumnExtType::Vector)
|
||||||
|
.map_err(|_| "Invalid dimension for Vector".to_string()),
|
||||||
|
_ => Err("Unknown variant".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&Field> for ColumnSchema {
|
impl TryFrom<&Field> for ColumnSchema {
|
||||||
@@ -272,11 +340,17 @@ impl TryFrom<&Field> for ColumnSchema {
|
|||||||
fn try_from(field: &Field) -> Result<ColumnSchema> {
|
fn try_from(field: &Field) -> Result<ColumnSchema> {
|
||||||
let mut data_type = ConcreteDataType::try_from(field.data_type())?;
|
let mut data_type = ConcreteDataType::try_from(field.data_type())?;
|
||||||
// Override the data type if it is specified in the metadata.
|
// Override the data type if it is specified in the metadata.
|
||||||
if field.metadata().contains_key(TYPE_KEY) {
|
if let Some(s) = field.metadata().get(TYPE_KEY) {
|
||||||
data_type = match field.metadata().get(TYPE_KEY).unwrap().as_str() {
|
let extype = ColumnExtType::from_str(s)
|
||||||
JSON_TYPE_NAME => ConcreteDataType::json_datatype(),
|
.map_err(|_| ParseExtendedTypeSnafu { value: s }.build())?;
|
||||||
_ => data_type,
|
match extype {
|
||||||
};
|
ColumnExtType::Json => {
|
||||||
|
data_type = ConcreteDataType::json_datatype();
|
||||||
|
}
|
||||||
|
ColumnExtType::Vector(dim) => {
|
||||||
|
data_type = ConcreteDataType::vector_datatype(dim);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let mut metadata = field.metadata().clone();
|
let mut metadata = field.metadata().clone();
|
||||||
let default_constraint = match metadata.remove(DEFAULT_CONSTRAINT_KEY) {
|
let default_constraint = match metadata.remove(DEFAULT_CONSTRAINT_KEY) {
|
||||||
@@ -328,7 +402,7 @@ impl TryFrom<&ColumnSchema> for Field {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Fulltext options for a column.
|
/// Fulltext options for a column.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, Visit, VisitMut)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct FulltextOptions {
|
pub struct FulltextOptions {
|
||||||
/// Whether the fulltext index is enabled.
|
/// Whether the fulltext index is enabled.
|
||||||
@@ -341,8 +415,71 @@ pub struct FulltextOptions {
|
|||||||
pub case_sensitive: bool,
|
pub case_sensitive: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FulltextOptions {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "enable={}", self.enable)?;
|
||||||
|
if self.enable {
|
||||||
|
write!(f, ", analyzer={}", self.analyzer)?;
|
||||||
|
write!(f, ", case_sensitive={}", self.case_sensitive)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<HashMap<String, String>> for FulltextOptions {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(options: HashMap<String, String>) -> Result<Self> {
|
||||||
|
let mut fulltext_options = FulltextOptions {
|
||||||
|
enable: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(enable) = options.get(COLUMN_FULLTEXT_CHANGE_OPT_KEY_ENABLE) {
|
||||||
|
match enable.to_ascii_lowercase().as_str() {
|
||||||
|
"true" => fulltext_options.enable = true,
|
||||||
|
"false" => fulltext_options.enable = false,
|
||||||
|
_ => {
|
||||||
|
return InvalidFulltextOptionSnafu {
|
||||||
|
msg: format!("{enable}, expected: 'true' | 'false'"),
|
||||||
|
}
|
||||||
|
.fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(analyzer) = options.get(COLUMN_FULLTEXT_OPT_KEY_ANALYZER) {
|
||||||
|
match analyzer.to_ascii_lowercase().as_str() {
|
||||||
|
"english" => fulltext_options.analyzer = FulltextAnalyzer::English,
|
||||||
|
"chinese" => fulltext_options.analyzer = FulltextAnalyzer::Chinese,
|
||||||
|
_ => {
|
||||||
|
return InvalidFulltextOptionSnafu {
|
||||||
|
msg: format!("{analyzer}, expected: 'English' | 'Chinese'"),
|
||||||
|
}
|
||||||
|
.fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(case_sensitive) = options.get(COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE) {
|
||||||
|
match case_sensitive.to_ascii_lowercase().as_str() {
|
||||||
|
"true" => fulltext_options.case_sensitive = true,
|
||||||
|
"false" => fulltext_options.case_sensitive = false,
|
||||||
|
_ => {
|
||||||
|
return InvalidFulltextOptionSnafu {
|
||||||
|
msg: format!("{case_sensitive}, expected: 'true' | 'false'"),
|
||||||
|
}
|
||||||
|
.fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(fulltext_options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Fulltext analyzer.
|
/// Fulltext analyzer.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, Visit, VisitMut)]
|
||||||
pub enum FulltextAnalyzer {
|
pub enum FulltextAnalyzer {
|
||||||
#[default]
|
#[default]
|
||||||
English,
|
English,
|
||||||
@@ -564,5 +701,24 @@ mod tests {
|
|||||||
column_schema.metadata.get(TYPE_KEY).unwrap(),
|
column_schema.metadata.get(TYPE_KEY).unwrap(),
|
||||||
&ConcreteDataType::json_datatype().name()
|
&ConcreteDataType::json_datatype().name()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let field = Field::new("test", ArrowDataType::Binary, true);
|
||||||
|
let field = field.with_metadata(Metadata::from([(
|
||||||
|
TYPE_KEY.to_string(),
|
||||||
|
ConcreteDataType::vector_datatype(3).name(),
|
||||||
|
)]));
|
||||||
|
let column_schema = ColumnSchema::try_from(&field).unwrap();
|
||||||
|
assert_eq!("test", column_schema.name);
|
||||||
|
assert_eq!(
|
||||||
|
ConcreteDataType::vector_datatype(3),
|
||||||
|
column_schema.data_type
|
||||||
|
);
|
||||||
|
assert!(column_schema.is_nullable);
|
||||||
|
assert!(!column_schema.is_time_index);
|
||||||
|
assert!(column_schema.default_constraint.is_none());
|
||||||
|
assert_eq!(
|
||||||
|
column_schema.metadata.get(TYPE_KEY).unwrap(),
|
||||||
|
&ConcreteDataType::vector_datatype(3).name()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ impl ColumnDefaultConstraint {
|
|||||||
// Whether the value could be nullable has been checked before, only need
|
// Whether the value could be nullable has been checked before, only need
|
||||||
// to check the type compatibility here.
|
// to check the type compatibility here.
|
||||||
ensure!(
|
ensure!(
|
||||||
data_type.logical_type_id() == v.logical_type_id(),
|
value_type_match(data_type, v.data_type()),
|
||||||
error::DefaultValueTypeSnafu {
|
error::DefaultValueTypeSnafu {
|
||||||
reason: format!(
|
reason: format!(
|
||||||
"column has type {:?} but default value has type {:?}",
|
"column has type {:?} but default value has type {:?}",
|
||||||
@@ -215,6 +215,17 @@ fn create_current_timestamp_vector(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn value_type_match(column_type: &ConcreteDataType, value_type: ConcreteDataType) -> bool {
|
||||||
|
match (column_type, value_type) {
|
||||||
|
(ct, vt) if ct.logical_type_id() == vt.logical_type_id() => true,
|
||||||
|
// Vector and Json type is encoded as binary
|
||||||
|
(ConcreteDataType::Vector(_) | ConcreteDataType::Json(_), ConcreteDataType::Binary(_)) => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ pub enum LogicalTypeId {
|
|||||||
Dictionary,
|
Dictionary,
|
||||||
|
|
||||||
Json,
|
Json,
|
||||||
|
|
||||||
|
Vector,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LogicalTypeId {
|
impl LogicalTypeId {
|
||||||
@@ -129,6 +131,7 @@ impl LogicalTypeId {
|
|||||||
LogicalTypeId::DurationNanosecond => ConcreteDataType::duration_nanosecond_datatype(),
|
LogicalTypeId::DurationNanosecond => ConcreteDataType::duration_nanosecond_datatype(),
|
||||||
LogicalTypeId::Decimal128 => ConcreteDataType::decimal128_default_datatype(),
|
LogicalTypeId::Decimal128 => ConcreteDataType::decimal128_default_datatype(),
|
||||||
LogicalTypeId::Json => ConcreteDataType::json_datatype(),
|
LogicalTypeId::Json => ConcreteDataType::json_datatype(),
|
||||||
|
LogicalTypeId::Vector => ConcreteDataType::vector_default_datatype(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ mod primitive_type;
|
|||||||
mod string_type;
|
mod string_type;
|
||||||
mod time_type;
|
mod time_type;
|
||||||
mod timestamp_type;
|
mod timestamp_type;
|
||||||
|
mod vector_type;
|
||||||
|
|
||||||
pub use binary_type::BinaryType;
|
pub use binary_type::BinaryType;
|
||||||
pub use boolean_type::BooleanType;
|
pub use boolean_type::BooleanType;
|
||||||
@@ -43,7 +44,9 @@ pub use duration_type::{
|
|||||||
pub use interval_type::{
|
pub use interval_type::{
|
||||||
IntervalDayTimeType, IntervalMonthDayNanoType, IntervalType, IntervalYearMonthType,
|
IntervalDayTimeType, IntervalMonthDayNanoType, IntervalType, IntervalYearMonthType,
|
||||||
};
|
};
|
||||||
pub use json_type::{JsonType, JSON_TYPE_NAME};
|
pub use json_type::{
|
||||||
|
json_type_value_to_string, parse_string_to_json_type_value, JsonType, JSON_TYPE_NAME,
|
||||||
|
};
|
||||||
pub use list_type::ListType;
|
pub use list_type::ListType;
|
||||||
pub use null_type::NullType;
|
pub use null_type::NullType;
|
||||||
pub use primitive_type::{
|
pub use primitive_type::{
|
||||||
@@ -58,3 +61,4 @@ pub use timestamp_type::{
|
|||||||
TimestampMicrosecondType, TimestampMillisecondType, TimestampNanosecondType,
|
TimestampMicrosecondType, TimestampMillisecondType, TimestampNanosecondType,
|
||||||
TimestampSecondType, TimestampType,
|
TimestampSecondType, TimestampType,
|
||||||
};
|
};
|
||||||
|
pub use vector_type::{parse_string_to_vector_type_value, vector_type_value_to_string, VectorType};
|
||||||
|
|||||||
@@ -12,13 +12,12 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use arrow::datatypes::DataType as ArrowDataType;
|
use arrow::datatypes::DataType as ArrowDataType;
|
||||||
use common_base::bytes::Bytes;
|
use common_base::bytes::Bytes;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::data_type::{DataType, DataTypeRef};
|
use crate::data_type::DataType;
|
||||||
|
use crate::error::{InvalidJsonSnafu, Result};
|
||||||
use crate::scalars::ScalarVectorBuilder;
|
use crate::scalars::ScalarVectorBuilder;
|
||||||
use crate::type_id::LogicalTypeId;
|
use crate::type_id::LogicalTypeId;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
@@ -26,14 +25,29 @@ use crate::vectors::{BinaryVectorBuilder, MutableVector};
|
|||||||
|
|
||||||
pub const JSON_TYPE_NAME: &str = "Json";
|
pub const JSON_TYPE_NAME: &str = "Json";
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
|
pub enum JsonFormat {
|
||||||
|
Jsonb,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for JsonFormat {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Jsonb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// JsonType is a data type for JSON data. It is stored as binary data of jsonb format.
|
/// JsonType is a data type for JSON data. It is stored as binary data of jsonb format.
|
||||||
/// It utilizes current binary value and vector implementation.
|
/// It utilizes current binary value and vector implementation.
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(
|
||||||
pub struct JsonType;
|
Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
|
||||||
|
)]
|
||||||
|
pub struct JsonType {
|
||||||
|
pub format: JsonFormat,
|
||||||
|
}
|
||||||
|
|
||||||
impl JsonType {
|
impl JsonType {
|
||||||
pub fn arc() -> DataTypeRef {
|
pub fn new(format: JsonFormat) -> Self {
|
||||||
Arc::new(Self)
|
Self { format }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,3 +79,19 @@ impl DataType for JsonType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts a json type value to string
|
||||||
|
pub fn json_type_value_to_string(val: &[u8], format: &JsonFormat) -> Result<String> {
|
||||||
|
match format {
|
||||||
|
JsonFormat::Jsonb => Ok(jsonb::to_string(val)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a string to a json type value
|
||||||
|
pub fn parse_string_to_json_type_value(s: &str, format: &JsonFormat) -> Result<Vec<u8>> {
|
||||||
|
match format {
|
||||||
|
JsonFormat::Jsonb => jsonb::parse_value(s.as_bytes())
|
||||||
|
.map_err(|_| InvalidJsonSnafu { value: s }.build())
|
||||||
|
.map(|json| json.to_vec()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
237
src/datatypes/src/types/vector_type.rs
Normal file
237
src/datatypes/src/types/vector_type.rs
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
// Copyright 2023 Greptime Team
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use arrow::datatypes::DataType as ArrowDataType;
|
||||||
|
use common_base::bytes::Bytes;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::data_type::DataType;
|
||||||
|
use crate::error::{InvalidVectorSnafu, Result};
|
||||||
|
use crate::scalars::ScalarVectorBuilder;
|
||||||
|
use crate::type_id::LogicalTypeId;
|
||||||
|
use crate::value::Value;
|
||||||
|
use crate::vectors::{BinaryVectorBuilder, MutableVector};
|
||||||
|
|
||||||
|
/// `VectorType` is a data type for vector data with a fixed dimension.
|
||||||
|
/// The type of items in the vector is float32.
|
||||||
|
/// It is stored as binary data that contains the concatenated float32 values.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
|
pub struct VectorType {
|
||||||
|
pub dim: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VectorType {
|
||||||
|
pub fn new(dim: u32) -> Self {
|
||||||
|
Self { dim }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataType for VectorType {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
format!("Vector({})", self.dim)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn logical_type_id(&self) -> LogicalTypeId {
|
||||||
|
LogicalTypeId::Vector
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_value(&self) -> Value {
|
||||||
|
Bytes::default().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_arrow_type(&self) -> ArrowDataType {
|
||||||
|
ArrowDataType::Binary
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_mutable_vector(&self, capacity: usize) -> Box<dyn MutableVector> {
|
||||||
|
Box::new(BinaryVectorBuilder::with_capacity(capacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_cast(&self, from: Value) -> Option<Value> {
|
||||||
|
match from {
|
||||||
|
Value::Binary(v) => Some(Value::Binary(v)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a vector type value to string
|
||||||
|
/// for example: [1.0, 2.0, 3.0] -> "[1.0,2.0,3.0]"
|
||||||
|
pub fn vector_type_value_to_string(val: &[u8], dim: u32) -> Result<String> {
|
||||||
|
let expected_len = dim as usize * std::mem::size_of::<f32>();
|
||||||
|
if val.len() != expected_len {
|
||||||
|
return InvalidVectorSnafu {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to convert Vector value to string: wrong byte size, expected {}, got {}",
|
||||||
|
expected_len,
|
||||||
|
val.len()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
.fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
if dim == 0 {
|
||||||
|
return Ok("[]".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let elements = unsafe {
|
||||||
|
std::slice::from_raw_parts(
|
||||||
|
val.as_ptr() as *const f32,
|
||||||
|
val.len() / std::mem::size_of::<f32>(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut s = String::from("[");
|
||||||
|
for (i, e) in elements.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
s.push(',');
|
||||||
|
}
|
||||||
|
s.push_str(&e.to_string());
|
||||||
|
}
|
||||||
|
s.push(']');
|
||||||
|
Ok(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a string to a vector type value
|
||||||
|
/// Valid input format: "[1.0,2.0,3.0]", "[1.0, 2.0, 3.0]"
|
||||||
|
pub fn parse_string_to_vector_type_value(s: &str, dim: u32) -> Result<Vec<u8>> {
|
||||||
|
// Trim the brackets
|
||||||
|
let trimmed = s.trim();
|
||||||
|
if !trimmed.starts_with('[') || !trimmed.ends_with(']') {
|
||||||
|
return InvalidVectorSnafu {
|
||||||
|
msg: format!("Failed to parse {s} to Vector value: not properly enclosed in brackets"),
|
||||||
|
}
|
||||||
|
.fail();
|
||||||
|
}
|
||||||
|
// Remove the brackets
|
||||||
|
let content = trimmed[1..trimmed.len() - 1].trim();
|
||||||
|
|
||||||
|
if content.is_empty() {
|
||||||
|
if dim != 0 {
|
||||||
|
return InvalidVectorSnafu {
|
||||||
|
msg: format!("Failed to parse {s} to Vector value: wrong dimension"),
|
||||||
|
}
|
||||||
|
.fail();
|
||||||
|
}
|
||||||
|
return Ok(vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let elements = content
|
||||||
|
.split(',')
|
||||||
|
.map(|e| {
|
||||||
|
e.trim().parse::<f32>().map_err(|_| {
|
||||||
|
InvalidVectorSnafu {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to parse {s} to Vector value: elements are not all float32"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<f32>>>()?;
|
||||||
|
|
||||||
|
// Check dimension
|
||||||
|
if elements.len() != dim as usize {
|
||||||
|
return InvalidVectorSnafu {
|
||||||
|
msg: format!("Failed to parse {s} to Vector value: wrong dimension"),
|
||||||
|
}
|
||||||
|
.fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert Vec<f32> to Vec<u8>
|
||||||
|
let bytes = unsafe {
|
||||||
|
std::slice::from_raw_parts(
|
||||||
|
elements.as_ptr() as *const u8,
|
||||||
|
elements.len() * std::mem::size_of::<f32>(),
|
||||||
|
)
|
||||||
|
.to_vec()
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_conversion_between_string_and_vector_type_value() {
|
||||||
|
let dim = 3;
|
||||||
|
|
||||||
|
let cases = [
|
||||||
|
("[1.0,2.0,3]", "[1,2,3]"),
|
||||||
|
("[0.0 , 0.0 , 0.0]", "[0,0,0]"),
|
||||||
|
("[3.4028235e38, -3.4028235e38, 1.1754944e-38]", "[340282350000000000000000000000000000000,-340282350000000000000000000000000000000,0.000000000000000000000000000000000000011754944]"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (s, expected) in cases.iter() {
|
||||||
|
let val = parse_string_to_vector_type_value(s, dim).unwrap();
|
||||||
|
let s = vector_type_value_to_string(&val, dim).unwrap();
|
||||||
|
assert_eq!(s, *expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dim = 0;
|
||||||
|
let cases = [("[]", "[]"), ("[ ]", "[]"), ("[ ]", "[]")];
|
||||||
|
for (s, expected) in cases.iter() {
|
||||||
|
let val = parse_string_to_vector_type_value(s, dim).unwrap();
|
||||||
|
let s = vector_type_value_to_string(&val, dim).unwrap();
|
||||||
|
assert_eq!(s, *expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vector_type_value_to_string_wrong_byte_size() {
|
||||||
|
let dim = 3;
|
||||||
|
let val = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
||||||
|
let res = vector_type_value_to_string(&val, dim);
|
||||||
|
assert!(res.is_err());
|
||||||
|
|
||||||
|
let dim = 0;
|
||||||
|
let val = vec![1];
|
||||||
|
let res = vector_type_value_to_string(&val, dim);
|
||||||
|
assert!(res.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_string_to_vector_type_value_not_properly_enclosed_in_brackets() {
|
||||||
|
let dim = 3;
|
||||||
|
let s = "1.0,2.0,3.0";
|
||||||
|
let res = parse_string_to_vector_type_value(s, dim);
|
||||||
|
assert!(res.is_err());
|
||||||
|
|
||||||
|
let s = "[1.0,2.0,3.0";
|
||||||
|
let res = parse_string_to_vector_type_value(s, dim);
|
||||||
|
assert!(res.is_err());
|
||||||
|
|
||||||
|
let s = "1.0,2.0,3.0]";
|
||||||
|
let res = parse_string_to_vector_type_value(s, dim);
|
||||||
|
assert!(res.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_string_to_vector_type_value_wrong_dimension() {
|
||||||
|
let dim = 3;
|
||||||
|
let s = "[1.0,2.0]";
|
||||||
|
let res = parse_string_to_vector_type_value(s, dim);
|
||||||
|
assert!(res.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_string_to_vector_type_value_elements_are_not_all_float32() {
|
||||||
|
let dim = 3;
|
||||||
|
let s = "[1.0,2.0,ah]";
|
||||||
|
let res = parse_string_to_vector_type_value(s, dim);
|
||||||
|
assert!(res.is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -520,7 +520,9 @@ pub fn to_null_scalar_value(output_type: &ConcreteDataType) -> Result<ScalarValu
|
|||||||
ConcreteDataType::UInt64(_) => ScalarValue::UInt64(None),
|
ConcreteDataType::UInt64(_) => ScalarValue::UInt64(None),
|
||||||
ConcreteDataType::Float32(_) => ScalarValue::Float32(None),
|
ConcreteDataType::Float32(_) => ScalarValue::Float32(None),
|
||||||
ConcreteDataType::Float64(_) => ScalarValue::Float64(None),
|
ConcreteDataType::Float64(_) => ScalarValue::Float64(None),
|
||||||
ConcreteDataType::Binary(_) | ConcreteDataType::Json(_) => ScalarValue::Binary(None),
|
ConcreteDataType::Binary(_) | ConcreteDataType::Json(_) | ConcreteDataType::Vector(_) => {
|
||||||
|
ScalarValue::Binary(None)
|
||||||
|
}
|
||||||
ConcreteDataType::String(_) => ScalarValue::Utf8(None),
|
ConcreteDataType::String(_) => ScalarValue::Utf8(None),
|
||||||
ConcreteDataType::Date(_) => ScalarValue::Date32(None),
|
ConcreteDataType::Date(_) => ScalarValue::Date32(None),
|
||||||
ConcreteDataType::DateTime(_) => ScalarValue::Date64(None),
|
ConcreteDataType::DateTime(_) => ScalarValue::Date64(None),
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ use snafu::ResultExt;
|
|||||||
|
|
||||||
use crate::arrow_array::{BinaryArray, MutableBinaryArray};
|
use crate::arrow_array::{BinaryArray, MutableBinaryArray};
|
||||||
use crate::data_type::ConcreteDataType;
|
use crate::data_type::ConcreteDataType;
|
||||||
use crate::error::{self, Result};
|
use crate::error::{self, InvalidVectorSnafu, Result};
|
||||||
use crate::scalars::{ScalarVector, ScalarVectorBuilder};
|
use crate::scalars::{ScalarVector, ScalarVectorBuilder};
|
||||||
use crate::serialize::Serializable;
|
use crate::serialize::Serializable;
|
||||||
|
use crate::types::parse_string_to_vector_type_value;
|
||||||
use crate::value::{Value, ValueRef};
|
use crate::value::{Value, ValueRef};
|
||||||
use crate::vectors::{self, MutableVector, Validity, Vector, VectorRef};
|
use crate::vectors::{self, MutableVector, Validity, Vector, VectorRef};
|
||||||
|
|
||||||
@@ -66,6 +67,40 @@ impl BinaryVector {
|
|||||||
}
|
}
|
||||||
Ok(BinaryVector::from(vector))
|
Ok(BinaryVector::from(vector))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn convert_binary_to_vector(&self, dim: u32) -> Result<BinaryVector> {
|
||||||
|
let arrow_array = self.to_arrow_array();
|
||||||
|
let mut vector = vec![];
|
||||||
|
for binary in arrow_array
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<BinaryArray>()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
{
|
||||||
|
let v = if let Some(binary) = binary {
|
||||||
|
let bytes_size = dim as usize * std::mem::size_of::<f32>();
|
||||||
|
if let Ok(s) = String::from_utf8(binary.to_vec()) {
|
||||||
|
let v = parse_string_to_vector_type_value(&s, dim)?;
|
||||||
|
Some(v)
|
||||||
|
} else if binary.len() == dim as usize * std::mem::size_of::<f32>() {
|
||||||
|
Some(binary.to_vec())
|
||||||
|
} else {
|
||||||
|
return InvalidVectorSnafu {
|
||||||
|
msg: format!(
|
||||||
|
"Unexpected bytes size for vector value, expected {}, got {}",
|
||||||
|
bytes_size,
|
||||||
|
binary.len()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
.fail();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
vector.push(v);
|
||||||
|
}
|
||||||
|
Ok(BinaryVector::from(vector))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BinaryArray> for BinaryVector {
|
impl From<BinaryArray> for BinaryVector {
|
||||||
@@ -462,5 +497,56 @@ mod tests {
|
|||||||
.convert_binary_to_json()
|
.convert_binary_to_json()
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
assert_matches!(error, error::Error::InvalidJson { .. });
|
assert_matches!(error, error::Error::InvalidJson { .. });
|
||||||
|
|
||||||
|
// corrupted jsonb
|
||||||
|
let jsonb = jsonb::parse_value("{\"hello\": \"world\"}".as_bytes())
|
||||||
|
.unwrap()
|
||||||
|
.to_vec();
|
||||||
|
let corrupted_jsonb = jsonb[0..jsonb.len() - 1].to_vec();
|
||||||
|
let error = BinaryVector::from(vec![corrupted_jsonb])
|
||||||
|
.convert_binary_to_json()
|
||||||
|
.unwrap_err();
|
||||||
|
assert_matches!(error, error::Error::InvalidJson { .. });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_binary_vector_conversion() {
|
||||||
|
let dim = 3;
|
||||||
|
let vector = BinaryVector::from(vec![
|
||||||
|
Some(b"[1,2,3]".to_vec()),
|
||||||
|
Some(b"[4,5,6]".to_vec()),
|
||||||
|
Some(b"[7,8,9]".to_vec()),
|
||||||
|
None,
|
||||||
|
]);
|
||||||
|
let expected = BinaryVector::from(vec![
|
||||||
|
Some(
|
||||||
|
[1.0f32, 2.0, 3.0]
|
||||||
|
.iter()
|
||||||
|
.flat_map(|v| v.to_le_bytes())
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
Some(
|
||||||
|
[4.0f32, 5.0, 6.0]
|
||||||
|
.iter()
|
||||||
|
.flat_map(|v| v.to_le_bytes())
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
Some(
|
||||||
|
[7.0f32, 8.0, 9.0]
|
||||||
|
.iter()
|
||||||
|
.flat_map(|v| v.to_le_bytes())
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let converted = vector.convert_binary_to_vector(dim).unwrap();
|
||||||
|
assert_eq!(converted.len(), expected.len());
|
||||||
|
for i in 0..3 {
|
||||||
|
assert_eq!(
|
||||||
|
converted.get_ref(i).as_binary().unwrap().unwrap(),
|
||||||
|
expected.get_ref(i).as_binary().unwrap().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ fn equal(lhs: &dyn Vector, rhs: &dyn Vector) -> bool {
|
|||||||
match lhs.data_type() {
|
match lhs.data_type() {
|
||||||
Null(_) => true,
|
Null(_) => true,
|
||||||
Boolean(_) => is_vector_eq!(BooleanVector, lhs, rhs),
|
Boolean(_) => is_vector_eq!(BooleanVector, lhs, rhs),
|
||||||
Binary(_) | Json(_) => is_vector_eq!(BinaryVector, lhs, rhs),
|
Binary(_) | Json(_) | Vector(_) => is_vector_eq!(BinaryVector, lhs, rhs),
|
||||||
String(_) => is_vector_eq!(StringVector, lhs, rhs),
|
String(_) => is_vector_eq!(StringVector, lhs, rhs),
|
||||||
Date(_) => is_vector_eq!(DateVector, lhs, rhs),
|
Date(_) => is_vector_eq!(DateVector, lhs, rhs),
|
||||||
DateTime(_) => is_vector_eq!(DateTimeVector, lhs, rhs),
|
DateTime(_) => is_vector_eq!(DateTimeVector, lhs, rhs),
|
||||||
|
|||||||
@@ -91,10 +91,17 @@ macro_rules! impl_scalar_vector_op {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn cast(&self, to_type: &ConcreteDataType) -> Result<VectorRef> {
|
fn cast(&self, to_type: &ConcreteDataType) -> Result<VectorRef> {
|
||||||
if to_type == &ConcreteDataType::json_datatype() {
|
if let Some(vector) = self.as_any().downcast_ref::<BinaryVector>() {
|
||||||
if let Some(vector) = self.as_any().downcast_ref::<BinaryVector>() {
|
match to_type {
|
||||||
let json_vector = vector.convert_binary_to_json()?;
|
ConcreteDataType::Json(_) => {
|
||||||
return Ok(Arc::new(json_vector) as VectorRef);
|
let json_vector = vector.convert_binary_to_json()?;
|
||||||
|
return Ok(Arc::new(json_vector) as VectorRef);
|
||||||
|
}
|
||||||
|
ConcreteDataType::Vector(d) => {
|
||||||
|
let vector = vector.convert_binary_to_vector(d.dim)?;
|
||||||
|
return Ok(Arc::new(vector) as VectorRef);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cast::cast_non_constant!(self, to_type)
|
cast::cast_non_constant!(self, to_type)
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ use operator::delete::Deleter;
|
|||||||
use operator::insert::Inserter;
|
use operator::insert::Inserter;
|
||||||
use operator::statement::StatementExecutor;
|
use operator::statement::StatementExecutor;
|
||||||
use partition::manager::PartitionRuleManager;
|
use partition::manager::PartitionRuleManager;
|
||||||
use query::stats::StatementStatistics;
|
|
||||||
use query::{QueryEngine, QueryEngineFactory};
|
use query::{QueryEngine, QueryEngineFactory};
|
||||||
use servers::error::{AlreadyStartedSnafu, StartGrpcSnafu, TcpBindSnafu, TcpIncomingSnafu};
|
use servers::error::{AlreadyStartedSnafu, StartGrpcSnafu, TcpBindSnafu, TcpIncomingSnafu};
|
||||||
use servers::server::Server;
|
use servers::server::Server;
|
||||||
@@ -303,7 +302,7 @@ impl FlownodeBuilder {
|
|||||||
///
|
///
|
||||||
/// or recover all existing flow tasks if in standalone mode(nodeid is None)
|
/// or recover all existing flow tasks if in standalone mode(nodeid is None)
|
||||||
///
|
///
|
||||||
/// TODO(discord9): persisent flow tasks with internal state
|
/// TODO(discord9): persistent flow tasks with internal state
|
||||||
async fn recover_flows(&self, manager: &FlowWorkerManagerRef) -> Result<usize, Error> {
|
async fn recover_flows(&self, manager: &FlowWorkerManagerRef) -> Result<usize, Error> {
|
||||||
let nodeid = self.opts.node_id;
|
let nodeid = self.opts.node_id;
|
||||||
let to_be_recovered: Vec<_> = if let Some(nodeid) = nodeid {
|
let to_be_recovered: Vec<_> = if let Some(nodeid) = nodeid {
|
||||||
@@ -476,7 +475,6 @@ impl FrontendInvoker {
|
|||||||
layered_cache_registry.clone(),
|
layered_cache_registry.clone(),
|
||||||
inserter.clone(),
|
inserter.clone(),
|
||||||
table_route_cache,
|
table_route_cache,
|
||||||
StatementStatistics::default(),
|
|
||||||
));
|
));
|
||||||
|
|
||||||
let invoker = FrontendInvoker::new(inserter, deleter, statement_executor);
|
let invoker = FrontendInvoker::new(inserter, deleter, statement_executor);
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ common-function.workspace = true
|
|||||||
common-grpc.workspace = true
|
common-grpc.workspace = true
|
||||||
common-macro.workspace = true
|
common-macro.workspace = true
|
||||||
common-meta.workspace = true
|
common-meta.workspace = true
|
||||||
|
common-options.workspace = true
|
||||||
common-procedure.workspace = true
|
common-procedure.workspace = true
|
||||||
common-query.workspace = true
|
common-query.workspace = true
|
||||||
common-recordbatch.workspace = true
|
common-recordbatch.workspace = true
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use common_config::config::Configurable;
|
use common_config::config::Configurable;
|
||||||
|
use common_options::datanode::DatanodeClientOptions;
|
||||||
use common_telemetry::logging::{LoggingOptions, TracingOptions};
|
use common_telemetry::logging::{LoggingOptions, TracingOptions};
|
||||||
use meta_client::MetaClientOptions;
|
use meta_client::MetaClientOptions;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -22,8 +23,7 @@ use servers::heartbeat_options::HeartbeatOptions;
|
|||||||
use servers::http::HttpOptions;
|
use servers::http::HttpOptions;
|
||||||
|
|
||||||
use crate::service_config::{
|
use crate::service_config::{
|
||||||
DatanodeOptions, InfluxdbOptions, MysqlOptions, OpentsdbOptions, OtlpOptions, PostgresOptions,
|
InfluxdbOptions, MysqlOptions, OpentsdbOptions, OtlpOptions, PostgresOptions, PromStoreOptions,
|
||||||
PromStoreOptions,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
@@ -42,7 +42,7 @@ pub struct FrontendOptions {
|
|||||||
pub otlp: OtlpOptions,
|
pub otlp: OtlpOptions,
|
||||||
pub meta_client: Option<MetaClientOptions>,
|
pub meta_client: Option<MetaClientOptions>,
|
||||||
pub logging: LoggingOptions,
|
pub logging: LoggingOptions,
|
||||||
pub datanode: DatanodeOptions,
|
pub datanode: DatanodeClientOptions,
|
||||||
pub user_provider: Option<String>,
|
pub user_provider: Option<String>,
|
||||||
pub export_metrics: ExportMetricsOption,
|
pub export_metrics: ExportMetricsOption,
|
||||||
pub tracing: TracingOptions,
|
pub tracing: TracingOptions,
|
||||||
@@ -64,7 +64,7 @@ impl Default for FrontendOptions {
|
|||||||
otlp: OtlpOptions::default(),
|
otlp: OtlpOptions::default(),
|
||||||
meta_client: None,
|
meta_client: None,
|
||||||
logging: LoggingOptions::default(),
|
logging: LoggingOptions::default(),
|
||||||
datanode: DatanodeOptions::default(),
|
datanode: DatanodeClientOptions::default(),
|
||||||
user_provider: None,
|
user_provider: None,
|
||||||
export_metrics: ExportMetricsOption::default(),
|
export_metrics: ExportMetricsOption::default(),
|
||||||
tracing: TracingOptions::default(),
|
tracing: TracingOptions::default(),
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ use query::metrics::OnDone;
|
|||||||
use query::parser::{PromQuery, QueryLanguageParser, QueryStatement};
|
use query::parser::{PromQuery, QueryLanguageParser, QueryStatement};
|
||||||
use query::query_engine::options::{validate_catalog_and_schema, QueryOptions};
|
use query::query_engine::options::{validate_catalog_and_schema, QueryOptions};
|
||||||
use query::query_engine::DescribeResult;
|
use query::query_engine::DescribeResult;
|
||||||
|
use query::stats::StatementStatistics;
|
||||||
use query::QueryEngineRef;
|
use query::QueryEngineRef;
|
||||||
use raft_engine::{Config, ReadableSize, RecoveryMode};
|
use raft_engine::{Config, ReadableSize, RecoveryMode};
|
||||||
use servers::error as server_error;
|
use servers::error as server_error;
|
||||||
@@ -122,6 +123,7 @@ pub struct Instance {
|
|||||||
deleter: DeleterRef,
|
deleter: DeleterRef,
|
||||||
export_metrics_task: Option<ExportMetricsTask>,
|
export_metrics_task: Option<ExportMetricsTask>,
|
||||||
table_metadata_manager: TableMetadataManagerRef,
|
table_metadata_manager: TableMetadataManagerRef,
|
||||||
|
stats: StatementStatistics,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
@@ -228,6 +230,10 @@ impl Instance {
|
|||||||
let query_interceptor = self.plugins.get::<SqlQueryInterceptorRef<Error>>();
|
let query_interceptor = self.plugins.get::<SqlQueryInterceptorRef<Error>>();
|
||||||
let query_interceptor = query_interceptor.as_ref();
|
let query_interceptor = query_interceptor.as_ref();
|
||||||
|
|
||||||
|
let _slow_query_timer = self
|
||||||
|
.stats
|
||||||
|
.start_slow_query_timer(QueryStatement::Sql(stmt.clone()));
|
||||||
|
|
||||||
let output = match stmt {
|
let output = match stmt {
|
||||||
Statement::Query(_) | Statement::Explain(_) | Statement::Delete(_) => {
|
Statement::Query(_) | Statement::Explain(_) | Statement::Delete(_) => {
|
||||||
let stmt = QueryStatement::Sql(stmt);
|
let stmt = QueryStatement::Sql(stmt);
|
||||||
@@ -412,7 +418,6 @@ impl PrometheusHandler for Instance {
|
|||||||
let interceptor = self
|
let interceptor = self
|
||||||
.plugins
|
.plugins
|
||||||
.get::<PromQueryInterceptorRef<server_error::Error>>();
|
.get::<PromQueryInterceptorRef<server_error::Error>>();
|
||||||
interceptor.pre_execute(query, query_ctx.clone())?;
|
|
||||||
|
|
||||||
self.plugins
|
self.plugins
|
||||||
.get::<PermissionCheckerRef>()
|
.get::<PermissionCheckerRef>()
|
||||||
@@ -426,9 +431,20 @@ impl PrometheusHandler for Instance {
|
|||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let _slow_query_timer = self.stats.start_slow_query_timer(stmt.clone());
|
||||||
|
|
||||||
|
let plan = self
|
||||||
|
.statement_executor
|
||||||
|
.plan(&stmt, query_ctx.clone())
|
||||||
|
.await
|
||||||
|
.map_err(BoxedError::new)
|
||||||
|
.context(ExecuteQuerySnafu)?;
|
||||||
|
|
||||||
|
interceptor.pre_execute(query, Some(&plan), query_ctx.clone())?;
|
||||||
|
|
||||||
let output = self
|
let output = self
|
||||||
.statement_executor
|
.statement_executor
|
||||||
.execute_stmt(stmt, query_ctx.clone())
|
.exec_plan(plan, query_ctx.clone())
|
||||||
.await
|
.await
|
||||||
.map_err(BoxedError::new)
|
.map_err(BoxedError::new)
|
||||||
.context(ExecuteQuerySnafu)?;
|
.context(ExecuteQuerySnafu)?;
|
||||||
|
|||||||
@@ -185,7 +185,6 @@ impl FrontendBuilder {
|
|||||||
local_cache_invalidator,
|
local_cache_invalidator,
|
||||||
inserter.clone(),
|
inserter.clone(),
|
||||||
table_route_cache,
|
table_route_cache,
|
||||||
self.stats,
|
|
||||||
));
|
));
|
||||||
|
|
||||||
let pipeline_operator = Arc::new(PipelineOperator::new(
|
let pipeline_operator = Arc::new(PipelineOperator::new(
|
||||||
@@ -211,6 +210,7 @@ impl FrontendBuilder {
|
|||||||
deleter,
|
deleter,
|
||||||
export_metrics_task: None,
|
export_metrics_task: None,
|
||||||
table_metadata_manager: Arc::new(TableMetadataManager::new(kv_backend)),
|
table_metadata_manager: Arc::new(TableMetadataManager::new(kv_backend)),
|
||||||
|
stats: self.stats,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use api::v1::{DeleteRequests, DropFlowExpr, InsertRequests, RowDeleteRequests, R
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use auth::{PermissionChecker, PermissionCheckerRef, PermissionReq};
|
use auth::{PermissionChecker, PermissionCheckerRef, PermissionReq};
|
||||||
use common_query::Output;
|
use common_query::Output;
|
||||||
use common_telemetry::tracing;
|
use common_telemetry::tracing::{self};
|
||||||
use query::parser::PromQuery;
|
use query::parser::PromQuery;
|
||||||
use servers::interceptor::{GrpcQueryInterceptor, GrpcQueryInterceptorRef};
|
use servers::interceptor::{GrpcQueryInterceptor, GrpcQueryInterceptorRef};
|
||||||
use servers::query_handler::grpc::GrpcQueryHandler;
|
use servers::query_handler::grpc::GrpcQueryHandler;
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ use pipeline::PipelineWay;
|
|||||||
use servers::error::{self, AuthSnafu, Result as ServerResult};
|
use servers::error::{self, AuthSnafu, Result as ServerResult};
|
||||||
use servers::interceptor::{OpenTelemetryProtocolInterceptor, OpenTelemetryProtocolInterceptorRef};
|
use servers::interceptor::{OpenTelemetryProtocolInterceptor, OpenTelemetryProtocolInterceptorRef};
|
||||||
use servers::otlp;
|
use servers::otlp;
|
||||||
use servers::otlp::plugin::TraceParserRef;
|
|
||||||
use servers::query_handler::OpenTelemetryProtocolHandler;
|
use servers::query_handler::OpenTelemetryProtocolHandler;
|
||||||
use session::context::QueryContextRef;
|
use session::context::QueryContextRef;
|
||||||
use snafu::ResultExt;
|
use snafu::ResultExt;
|
||||||
@@ -64,6 +63,7 @@ impl OpenTelemetryProtocolHandler for Instance {
|
|||||||
async fn traces(
|
async fn traces(
|
||||||
&self,
|
&self,
|
||||||
request: ExportTraceServiceRequest,
|
request: ExportTraceServiceRequest,
|
||||||
|
table_name: String,
|
||||||
ctx: QueryContextRef,
|
ctx: QueryContextRef,
|
||||||
) -> ServerResult<Output> {
|
) -> ServerResult<Output> {
|
||||||
self.plugins
|
self.plugins
|
||||||
@@ -77,13 +77,7 @@ impl OpenTelemetryProtocolHandler for Instance {
|
|||||||
.get::<OpenTelemetryProtocolInterceptorRef<servers::error::Error>>();
|
.get::<OpenTelemetryProtocolInterceptorRef<servers::error::Error>>();
|
||||||
interceptor_ref.pre_execute(ctx.clone())?;
|
interceptor_ref.pre_execute(ctx.clone())?;
|
||||||
|
|
||||||
let (table_name, spans) = match self.plugins.get::<TraceParserRef>() {
|
let spans = otlp::trace::parse(request);
|
||||||
Some(parser) => (parser.table_name(), parser.parse(request)),
|
|
||||||
None => (
|
|
||||||
otlp::trace::TRACE_TABLE_NAME.to_string(),
|
|
||||||
otlp::trace::parse(request),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
let (requests, rows) = otlp::trace::to_grpc_insert_requests(table_name, spans)?;
|
let (requests, rows) = otlp::trace::to_grpc_insert_requests(table_name, spans)?;
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
pub mod datanode;
|
|
||||||
pub mod influxdb;
|
pub mod influxdb;
|
||||||
pub mod mysql;
|
pub mod mysql;
|
||||||
pub mod opentsdb;
|
pub mod opentsdb;
|
||||||
@@ -26,5 +25,3 @@ pub use opentsdb::OpentsdbOptions;
|
|||||||
pub use otlp::OtlpOptions;
|
pub use otlp::OtlpOptions;
|
||||||
pub use postgres::PostgresOptions;
|
pub use postgres::PostgresOptions;
|
||||||
pub use prom_store::PromStoreOptions;
|
pub use prom_store::PromStoreOptions;
|
||||||
|
|
||||||
pub use self::datanode::DatanodeOptions;
|
|
||||||
|
|||||||
@@ -80,9 +80,7 @@ impl<R: RangeReader> InvertedIndexReader for InvertedIndexBlobReader<R> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use common_base::bit_vec::prelude::*;
|
use common_base::bit_vec::prelude::*;
|
||||||
use common_base::range_read::RangeReaderAdapter;
|
|
||||||
use fst::MapBuilder;
|
use fst::MapBuilder;
|
||||||
use futures::io::Cursor;
|
|
||||||
use greptime_proto::v1::index::{InvertedIndexMeta, InvertedIndexMetas};
|
use greptime_proto::v1::index::{InvertedIndexMeta, InvertedIndexMetas};
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
|
|
||||||
@@ -163,8 +161,7 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_inverted_index_blob_reader_metadata() {
|
async fn test_inverted_index_blob_reader_metadata() {
|
||||||
let blob = create_inverted_index_blob();
|
let blob = create_inverted_index_blob();
|
||||||
let cursor = RangeReaderAdapter(Cursor::new(blob));
|
let mut blob_reader = InvertedIndexBlobReader::new(blob);
|
||||||
let mut blob_reader = InvertedIndexBlobReader::new(cursor);
|
|
||||||
|
|
||||||
let metas = blob_reader.metadata().await.unwrap();
|
let metas = blob_reader.metadata().await.unwrap();
|
||||||
assert_eq!(metas.metas.len(), 2);
|
assert_eq!(metas.metas.len(), 2);
|
||||||
@@ -191,8 +188,7 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_inverted_index_blob_reader_fst() {
|
async fn test_inverted_index_blob_reader_fst() {
|
||||||
let blob = create_inverted_index_blob();
|
let blob = create_inverted_index_blob();
|
||||||
let cursor = RangeReaderAdapter(Cursor::new(blob));
|
let mut blob_reader = InvertedIndexBlobReader::new(blob);
|
||||||
let mut blob_reader = InvertedIndexBlobReader::new(cursor);
|
|
||||||
|
|
||||||
let metas = blob_reader.metadata().await.unwrap();
|
let metas = blob_reader.metadata().await.unwrap();
|
||||||
let meta = metas.metas.get("tag0").unwrap();
|
let meta = metas.metas.get("tag0").unwrap();
|
||||||
@@ -224,8 +220,7 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_inverted_index_blob_reader_bitmap() {
|
async fn test_inverted_index_blob_reader_bitmap() {
|
||||||
let blob = create_inverted_index_blob();
|
let blob = create_inverted_index_blob();
|
||||||
let cursor = RangeReaderAdapter(Cursor::new(blob));
|
let mut blob_reader = InvertedIndexBlobReader::new(blob);
|
||||||
let mut blob_reader = InvertedIndexBlobReader::new(cursor);
|
|
||||||
|
|
||||||
let metas = blob_reader.metadata().await.unwrap();
|
let metas = blob_reader.metadata().await.unwrap();
|
||||||
let meta = metas.metas.get("tag0").unwrap();
|
let meta = metas.metas.get("tag0").unwrap();
|
||||||
|
|||||||
@@ -113,8 +113,6 @@ impl<R: RangeReader> InvertedIndeFooterReader<R> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use common_base::range_read::RangeReaderAdapter;
|
|
||||||
use futures::io::Cursor;
|
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -141,10 +139,9 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let payload_buf = create_test_payload(meta);
|
let mut payload_buf = create_test_payload(meta);
|
||||||
let blob_size = payload_buf.len() as u64;
|
let blob_size = payload_buf.len() as u64;
|
||||||
let cursor = RangeReaderAdapter(Cursor::new(payload_buf));
|
let mut reader = InvertedIndeFooterReader::new(&mut payload_buf, blob_size);
|
||||||
let mut reader = InvertedIndeFooterReader::new(cursor, blob_size);
|
|
||||||
|
|
||||||
let payload_size = reader.read_payload_size().await.unwrap();
|
let payload_size = reader.read_payload_size().await.unwrap();
|
||||||
let metas = reader.read_payload(payload_size).await.unwrap();
|
let metas = reader.read_payload(payload_size).await.unwrap();
|
||||||
@@ -164,8 +161,7 @@ mod tests {
|
|||||||
let mut payload_buf = create_test_payload(meta);
|
let mut payload_buf = create_test_payload(meta);
|
||||||
payload_buf.push(0xff); // Add an extra byte to corrupt the footer
|
payload_buf.push(0xff); // Add an extra byte to corrupt the footer
|
||||||
let blob_size = payload_buf.len() as u64;
|
let blob_size = payload_buf.len() as u64;
|
||||||
let cursor = RangeReaderAdapter(Cursor::new(payload_buf));
|
let mut reader = InvertedIndeFooterReader::new(&mut payload_buf, blob_size);
|
||||||
let mut reader = InvertedIndeFooterReader::new(cursor, blob_size);
|
|
||||||
|
|
||||||
let payload_size_result = reader.read_payload_size().await;
|
let payload_size_result = reader.read_payload_size().await;
|
||||||
assert!(payload_size_result.is_err());
|
assert!(payload_size_result.is_err());
|
||||||
@@ -180,10 +176,9 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let payload_buf = create_test_payload(meta);
|
let mut payload_buf = create_test_payload(meta);
|
||||||
let blob_size = payload_buf.len() as u64;
|
let blob_size = payload_buf.len() as u64;
|
||||||
let cursor = RangeReaderAdapter(Cursor::new(payload_buf));
|
let mut reader = InvertedIndeFooterReader::new(&mut payload_buf, blob_size);
|
||||||
let mut reader = InvertedIndeFooterReader::new(cursor, blob_size);
|
|
||||||
|
|
||||||
let payload_size = reader.read_payload_size().await.unwrap();
|
let payload_size = reader.read_payload_size().await.unwrap();
|
||||||
let payload_result = reader.read_payload(payload_size).await;
|
let payload_result = reader.read_payload(payload_size).await;
|
||||||
|
|||||||
@@ -99,8 +99,6 @@ impl<W: AsyncWrite + Send + Unpin> InvertedIndexBlobWriter<W> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use common_base::range_read::RangeReaderAdapter;
|
|
||||||
use futures::io::Cursor;
|
|
||||||
use futures::stream;
|
use futures::stream;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -120,8 +118,7 @@ mod tests {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let cursor = RangeReaderAdapter(Cursor::new(blob));
|
let mut reader = InvertedIndexBlobReader::new(blob);
|
||||||
let mut reader = InvertedIndexBlobReader::new(cursor);
|
|
||||||
let metadata = reader.metadata().await.unwrap();
|
let metadata = reader.metadata().await.unwrap();
|
||||||
assert_eq!(metadata.total_row_count, 8);
|
assert_eq!(metadata.total_row_count, 8);
|
||||||
assert_eq!(metadata.segment_row_count, 1);
|
assert_eq!(metadata.segment_row_count, 1);
|
||||||
@@ -161,8 +158,7 @@ mod tests {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let cursor = RangeReaderAdapter(Cursor::new(blob));
|
let mut reader = InvertedIndexBlobReader::new(blob);
|
||||||
let mut reader = InvertedIndexBlobReader::new(cursor);
|
|
||||||
let metadata = reader.metadata().await.unwrap();
|
let metadata = reader.metadata().await.unwrap();
|
||||||
assert_eq!(metadata.total_row_count, 8);
|
assert_eq!(metadata.total_row_count, 8);
|
||||||
assert_eq!(metadata.segment_row_count, 1);
|
assert_eq!(metadata.segment_row_count, 1);
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ common-grpc.workspace = true
|
|||||||
common-macro.workspace = true
|
common-macro.workspace = true
|
||||||
common-meta.workspace = true
|
common-meta.workspace = true
|
||||||
common-telemetry.workspace = true
|
common-telemetry.workspace = true
|
||||||
|
futures.workspace = true
|
||||||
|
futures-util.workspace = true
|
||||||
humantime-serde.workspace = true
|
humantime-serde.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ mod cluster;
|
|||||||
mod store;
|
mod store;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use api::v1::meta::{ProcedureDetailResponse, Role};
|
use api::v1::meta::{ProcedureDetailResponse, Role};
|
||||||
use cluster::Client as ClusterClient;
|
use cluster::Client as ClusterClient;
|
||||||
use common_error::ext::BoxedError;
|
use common_error::ext::BoxedError;
|
||||||
@@ -30,7 +32,8 @@ use common_meta::cluster::{
|
|||||||
};
|
};
|
||||||
use common_meta::datanode::{DatanodeStatKey, DatanodeStatValue, RegionStat};
|
use common_meta::datanode::{DatanodeStatKey, DatanodeStatValue, RegionStat};
|
||||||
use common_meta::ddl::{ExecutorContext, ProcedureExecutor};
|
use common_meta::ddl::{ExecutorContext, ProcedureExecutor};
|
||||||
use common_meta::error::{self as meta_error, Result as MetaResult};
|
use common_meta::error::{self as meta_error, ExternalSnafu, Result as MetaResult};
|
||||||
|
use common_meta::range_stream::PaginationStream;
|
||||||
use common_meta::rpc::ddl::{SubmitDdlTaskRequest, SubmitDdlTaskResponse};
|
use common_meta::rpc::ddl::{SubmitDdlTaskRequest, SubmitDdlTaskResponse};
|
||||||
use common_meta::rpc::procedure::{
|
use common_meta::rpc::procedure::{
|
||||||
MigrateRegionRequest, MigrateRegionResponse, ProcedureStateResponse,
|
MigrateRegionRequest, MigrateRegionResponse, ProcedureStateResponse,
|
||||||
@@ -40,8 +43,10 @@ use common_meta::rpc::store::{
|
|||||||
BatchPutResponse, CompareAndPutRequest, CompareAndPutResponse, DeleteRangeRequest,
|
BatchPutResponse, CompareAndPutRequest, CompareAndPutResponse, DeleteRangeRequest,
|
||||||
DeleteRangeResponse, PutRequest, PutResponse, RangeRequest, RangeResponse,
|
DeleteRangeResponse, PutRequest, PutResponse, RangeRequest, RangeResponse,
|
||||||
};
|
};
|
||||||
|
use common_meta::rpc::KeyValue;
|
||||||
use common_meta::ClusterId;
|
use common_meta::ClusterId;
|
||||||
use common_telemetry::info;
|
use common_telemetry::info;
|
||||||
|
use futures::TryStreamExt;
|
||||||
use heartbeat::Client as HeartbeatClient;
|
use heartbeat::Client as HeartbeatClient;
|
||||||
use procedure::Client as ProcedureClient;
|
use procedure::Client as ProcedureClient;
|
||||||
use snafu::{OptionExt, ResultExt};
|
use snafu::{OptionExt, ResultExt};
|
||||||
@@ -314,16 +319,15 @@ impl ClusterInfo for MetaClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn list_region_stats(&self) -> Result<Vec<RegionStat>> {
|
async fn list_region_stats(&self) -> Result<Vec<RegionStat>> {
|
||||||
let cluster_client = self.cluster_client()?;
|
let cluster_kv_backend = Arc::new(self.cluster_client()?);
|
||||||
let range_prefix = DatanodeStatKey::key_prefix_with_cluster_id(self.id.0);
|
let range_prefix = DatanodeStatKey::key_prefix_with_cluster_id(self.id.0);
|
||||||
let req = RangeRequest::new().with_prefix(range_prefix);
|
let req = RangeRequest::new().with_prefix(range_prefix);
|
||||||
let mut datanode_stats = cluster_client
|
let stream = PaginationStream::new(cluster_kv_backend, req, 256, Arc::new(decode_stats))
|
||||||
.range(req)
|
.into_stream();
|
||||||
.await?
|
let mut datanode_stats = stream
|
||||||
.kvs
|
.try_collect::<Vec<_>>()
|
||||||
.into_iter()
|
.await
|
||||||
.map(|kv| DatanodeStatValue::try_from(kv.value).context(ConvertMetaRequestSnafu))
|
.context(ConvertMetaResponseSnafu)?;
|
||||||
.collect::<Result<Vec<_>>>()?;
|
|
||||||
let region_stats = datanode_stats
|
let region_stats = datanode_stats
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.flat_map(|datanode_stat| {
|
.flat_map(|datanode_stat| {
|
||||||
@@ -336,6 +340,12 @@ impl ClusterInfo for MetaClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn decode_stats(kv: KeyValue) -> MetaResult<DatanodeStatValue> {
|
||||||
|
DatanodeStatValue::try_from(kv.value)
|
||||||
|
.map_err(BoxedError::new)
|
||||||
|
.context(ExternalSnafu)
|
||||||
|
}
|
||||||
|
|
||||||
impl MetaClient {
|
impl MetaClient {
|
||||||
pub fn new(id: Id) -> Self {
|
pub fn new(id: Id) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
@@ -12,14 +12,22 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use std::any::Any;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use api::greptime_proto::v1;
|
use api::greptime_proto::v1;
|
||||||
use api::v1::meta::cluster_client::ClusterClient;
|
use api::v1::meta::cluster_client::ClusterClient;
|
||||||
use api::v1::meta::{MetasrvNodeInfo, MetasrvPeersRequest, ResponseHeader, Role};
|
use api::v1::meta::{MetasrvNodeInfo, MetasrvPeersRequest, ResponseHeader, Role};
|
||||||
|
use common_error::ext::BoxedError;
|
||||||
use common_grpc::channel_manager::ChannelManager;
|
use common_grpc::channel_manager::ChannelManager;
|
||||||
use common_meta::rpc::store::{BatchGetRequest, BatchGetResponse, RangeRequest, RangeResponse};
|
use common_meta::error::{Error as MetaError, ExternalSnafu, Result as MetaResult};
|
||||||
|
use common_meta::kv_backend::{KvBackend, TxnService};
|
||||||
|
use common_meta::rpc::store::{
|
||||||
|
BatchDeleteRequest, BatchDeleteResponse, BatchGetRequest, BatchGetResponse, BatchPutRequest,
|
||||||
|
BatchPutResponse, DeleteRangeRequest, DeleteRangeResponse, PutRequest, PutResponse,
|
||||||
|
RangeRequest, RangeResponse,
|
||||||
|
};
|
||||||
use common_telemetry::{info, warn};
|
use common_telemetry::{info, warn};
|
||||||
use snafu::{ensure, ResultExt};
|
use snafu::{ensure, ResultExt};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
@@ -79,6 +87,51 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TxnService for Client {
|
||||||
|
type Error = MetaError;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl KvBackend for Client {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"ClusterClientKvBackend"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn range(&self, req: RangeRequest) -> MetaResult<RangeResponse> {
|
||||||
|
self.range(req)
|
||||||
|
.await
|
||||||
|
.map_err(BoxedError::new)
|
||||||
|
.context(ExternalSnafu)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn put(&self, _: PutRequest) -> MetaResult<PutResponse> {
|
||||||
|
unimplemented!("`put` is not supported in cluster client kv backend")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn batch_put(&self, _: BatchPutRequest) -> MetaResult<BatchPutResponse> {
|
||||||
|
unimplemented!("`batch_put` is not supported in cluster client kv backend")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn batch_get(&self, req: BatchGetRequest) -> MetaResult<BatchGetResponse> {
|
||||||
|
self.batch_get(req)
|
||||||
|
.await
|
||||||
|
.map_err(BoxedError::new)
|
||||||
|
.context(ExternalSnafu)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_range(&self, _: DeleteRangeRequest) -> MetaResult<DeleteRangeResponse> {
|
||||||
|
unimplemented!("`delete_range` is not supported in cluster client kv backend")
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn batch_delete(&self, _: BatchDeleteRequest) -> MetaResult<BatchDeleteResponse> {
|
||||||
|
unimplemented!("`batch_delete` is not supported in cluster client kv backend")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Inner {
|
struct Inner {
|
||||||
id: Id,
|
id: Id,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ common-greptimedb-telemetry.workspace = true
|
|||||||
common-grpc.workspace = true
|
common-grpc.workspace = true
|
||||||
common-macro.workspace = true
|
common-macro.workspace = true
|
||||||
common-meta.workspace = true
|
common-meta.workspace = true
|
||||||
|
common-options.workspace = true
|
||||||
common-procedure.workspace = true
|
common-procedure.workspace = true
|
||||||
common-runtime.workspace = true
|
common-runtime.workspace = true
|
||||||
common-telemetry.workspace = true
|
common-telemetry.workspace = true
|
||||||
|
|||||||
@@ -478,6 +478,11 @@ pub struct HeartbeatHandlerGroupBuilder {
|
|||||||
/// The handler to handle region lease.
|
/// The handler to handle region lease.
|
||||||
region_lease_handler: Option<RegionLeaseHandler>,
|
region_lease_handler: Option<RegionLeaseHandler>,
|
||||||
|
|
||||||
|
/// The factor that determines how often statistics should be flushed,
|
||||||
|
/// based on the number of received heartbeats. When the number of heartbeats
|
||||||
|
/// reaches this factor, a flush operation is triggered.
|
||||||
|
flush_stats_factor: Option<usize>,
|
||||||
|
|
||||||
/// The plugins.
|
/// The plugins.
|
||||||
plugins: Option<Plugins>,
|
plugins: Option<Plugins>,
|
||||||
|
|
||||||
@@ -493,6 +498,7 @@ impl HeartbeatHandlerGroupBuilder {
|
|||||||
Self {
|
Self {
|
||||||
region_failure_handler: None,
|
region_failure_handler: None,
|
||||||
region_lease_handler: None,
|
region_lease_handler: None,
|
||||||
|
flush_stats_factor: None,
|
||||||
plugins: None,
|
plugins: None,
|
||||||
pushers,
|
pushers,
|
||||||
handlers: vec![],
|
handlers: vec![],
|
||||||
@@ -510,6 +516,12 @@ impl HeartbeatHandlerGroupBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the flush stats factor.
|
||||||
|
pub fn with_flush_stats_factor(mut self, flush_stats_factor: Option<usize>) -> Self {
|
||||||
|
self.flush_stats_factor = flush_stats_factor;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the [`Plugins`].
|
/// Sets the [`Plugins`].
|
||||||
pub fn with_plugins(mut self, plugins: Option<Plugins>) -> Self {
|
pub fn with_plugins(mut self, plugins: Option<Plugins>) -> Self {
|
||||||
self.plugins = plugins;
|
self.plugins = plugins;
|
||||||
@@ -550,7 +562,7 @@ impl HeartbeatHandlerGroupBuilder {
|
|||||||
if let Some(publish_heartbeat_handler) = publish_heartbeat_handler {
|
if let Some(publish_heartbeat_handler) = publish_heartbeat_handler {
|
||||||
self.add_handler_last(publish_heartbeat_handler);
|
self.add_handler_last(publish_heartbeat_handler);
|
||||||
}
|
}
|
||||||
self.add_handler_last(CollectStatsHandler::default());
|
self.add_handler_last(CollectStatsHandler::new(self.flush_stats_factor));
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ use crate::error::{self, Result};
|
|||||||
use crate::handler::{HandleControl, HeartbeatAccumulator, HeartbeatHandler};
|
use crate::handler::{HandleControl, HeartbeatAccumulator, HeartbeatHandler};
|
||||||
use crate::metasrv::Context;
|
use crate::metasrv::Context;
|
||||||
|
|
||||||
const MAX_CACHED_STATS_PER_KEY: usize = 10;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct EpochStats {
|
struct EpochStats {
|
||||||
stats: Vec<Stat>,
|
stats: Vec<Stat>,
|
||||||
@@ -69,9 +67,26 @@ impl EpochStats {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
const DEFAULT_FLUSH_STATS_FACTOR: usize = 3;
|
||||||
|
|
||||||
pub struct CollectStatsHandler {
|
pub struct CollectStatsHandler {
|
||||||
stats_cache: DashMap<DatanodeStatKey, EpochStats>,
|
stats_cache: DashMap<DatanodeStatKey, EpochStats>,
|
||||||
|
flush_stats_factor: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CollectStatsHandler {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CollectStatsHandler {
|
||||||
|
pub fn new(flush_stats_factor: Option<usize>) -> Self {
|
||||||
|
Self {
|
||||||
|
flush_stats_factor: flush_stats_factor.unwrap_or(DEFAULT_FLUSH_STATS_FACTOR),
|
||||||
|
stats_cache: DashMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
@@ -130,7 +145,7 @@ impl HeartbeatHandler for CollectStatsHandler {
|
|||||||
rewrite_node_address(ctx, last).await;
|
rewrite_node_address(ctx, last).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !refresh && epoch_stats.len() < MAX_CACHED_STATS_PER_KEY {
|
if !refresh && epoch_stats.len() < self.flush_stats_factor {
|
||||||
return Ok(HandleControl::Continue);
|
return Ok(HandleControl::Continue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,8 +276,7 @@ mod tests {
|
|||||||
let res = ctx.in_memory.get(&key).await.unwrap();
|
let res = ctx.in_memory.get(&key).await.unwrap();
|
||||||
let kv = res.unwrap();
|
let kv = res.unwrap();
|
||||||
let val: DatanodeStatValue = kv.value.try_into().unwrap();
|
let val: DatanodeStatValue = kv.value.try_into().unwrap();
|
||||||
// refresh every 10 stats
|
assert_eq!(handler.flush_stats_factor, val.stats.len());
|
||||||
assert_eq!(10, val.stats.len());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_request_many_times(
|
async fn handle_request_many_times(
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ use common_base::readable_size::ReadableSize;
|
|||||||
use common_base::Plugins;
|
use common_base::Plugins;
|
||||||
use common_config::Configurable;
|
use common_config::Configurable;
|
||||||
use common_greptimedb_telemetry::GreptimeDBTelemetryTask;
|
use common_greptimedb_telemetry::GreptimeDBTelemetryTask;
|
||||||
use common_grpc::channel_manager;
|
|
||||||
use common_meta::cache_invalidator::CacheInvalidatorRef;
|
use common_meta::cache_invalidator::CacheInvalidatorRef;
|
||||||
use common_meta::ddl::ProcedureExecutorRef;
|
use common_meta::ddl::ProcedureExecutorRef;
|
||||||
use common_meta::key::TableMetadataManagerRef;
|
use common_meta::key::TableMetadataManagerRef;
|
||||||
@@ -36,6 +35,7 @@ use common_meta::peer::Peer;
|
|||||||
use common_meta::region_keeper::MemoryRegionKeeperRef;
|
use common_meta::region_keeper::MemoryRegionKeeperRef;
|
||||||
use common_meta::wal_options_allocator::WalOptionsAllocatorRef;
|
use common_meta::wal_options_allocator::WalOptionsAllocatorRef;
|
||||||
use common_meta::{distributed_time_constants, ClusterId};
|
use common_meta::{distributed_time_constants, ClusterId};
|
||||||
|
use common_options::datanode::DatanodeClientOptions;
|
||||||
use common_procedure::options::ProcedureConfig;
|
use common_procedure::options::ProcedureConfig;
|
||||||
use common_procedure::ProcedureManagerRef;
|
use common_procedure::ProcedureManagerRef;
|
||||||
use common_telemetry::logging::{LoggingOptions, TracingOptions};
|
use common_telemetry::logging::{LoggingOptions, TracingOptions};
|
||||||
@@ -107,7 +107,7 @@ pub struct MetasrvOptions {
|
|||||||
/// The failure detector options.
|
/// The failure detector options.
|
||||||
pub failure_detector: PhiAccrualFailureDetectorOptions,
|
pub failure_detector: PhiAccrualFailureDetectorOptions,
|
||||||
/// The datanode options.
|
/// The datanode options.
|
||||||
pub datanode: DatanodeOptions,
|
pub datanode: DatanodeClientOptions,
|
||||||
/// Whether to enable telemetry.
|
/// Whether to enable telemetry.
|
||||||
pub enable_telemetry: bool,
|
pub enable_telemetry: bool,
|
||||||
/// The data home directory.
|
/// The data home directory.
|
||||||
@@ -130,6 +130,10 @@ pub struct MetasrvOptions {
|
|||||||
/// limit the number of operations in a txn because an infinitely large txn could
|
/// limit the number of operations in a txn because an infinitely large txn could
|
||||||
/// potentially block other operations.
|
/// potentially block other operations.
|
||||||
pub max_txn_ops: usize,
|
pub max_txn_ops: usize,
|
||||||
|
/// The factor that determines how often statistics should be flushed,
|
||||||
|
/// based on the number of received heartbeats. When the number of heartbeats
|
||||||
|
/// reaches this factor, a flush operation is triggered.
|
||||||
|
pub flush_stats_factor: usize,
|
||||||
/// The tracing options.
|
/// The tracing options.
|
||||||
pub tracing: TracingOptions,
|
pub tracing: TracingOptions,
|
||||||
/// The datastore for kv metadata.
|
/// The datastore for kv metadata.
|
||||||
@@ -158,13 +162,14 @@ impl Default for MetasrvOptions {
|
|||||||
max_metadata_value_size: Some(ReadableSize::kb(1500)),
|
max_metadata_value_size: Some(ReadableSize::kb(1500)),
|
||||||
},
|
},
|
||||||
failure_detector: PhiAccrualFailureDetectorOptions::default(),
|
failure_detector: PhiAccrualFailureDetectorOptions::default(),
|
||||||
datanode: DatanodeOptions::default(),
|
datanode: DatanodeClientOptions::default(),
|
||||||
enable_telemetry: true,
|
enable_telemetry: true,
|
||||||
data_home: METASRV_HOME.to_string(),
|
data_home: METASRV_HOME.to_string(),
|
||||||
wal: MetasrvWalConfig::default(),
|
wal: MetasrvWalConfig::default(),
|
||||||
export_metrics: ExportMetricsOption::default(),
|
export_metrics: ExportMetricsOption::default(),
|
||||||
store_key_prefix: String::new(),
|
store_key_prefix: String::new(),
|
||||||
max_txn_ops: 128,
|
max_txn_ops: 128,
|
||||||
|
flush_stats_factor: 3,
|
||||||
tracing: TracingOptions::default(),
|
tracing: TracingOptions::default(),
|
||||||
backend: BackendImpl::EtcdStore,
|
backend: BackendImpl::EtcdStore,
|
||||||
}
|
}
|
||||||
@@ -180,35 +185,6 @@ impl Configurable for MetasrvOptions {
|
|||||||
pub struct MetasrvInfo {
|
pub struct MetasrvInfo {
|
||||||
pub server_addr: String,
|
pub server_addr: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options for datanode.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
|
||||||
pub struct DatanodeOptions {
|
|
||||||
pub client: DatanodeClientOptions,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Options for datanode client.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
pub struct DatanodeClientOptions {
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub timeout: Duration,
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub connect_timeout: Duration,
|
|
||||||
pub tcp_nodelay: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for DatanodeClientOptions {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
timeout: Duration::from_secs(channel_manager::DEFAULT_GRPC_REQUEST_TIMEOUT_SECS),
|
|
||||||
connect_timeout: Duration::from_secs(
|
|
||||||
channel_manager::DEFAULT_GRPC_CONNECT_TIMEOUT_SECS,
|
|
||||||
),
|
|
||||||
tcp_nodelay: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub server_addr: String,
|
pub server_addr: String,
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ impl MetasrvBuilder {
|
|||||||
))
|
))
|
||||||
});
|
});
|
||||||
let flow_metadata_allocator = {
|
let flow_metadata_allocator = {
|
||||||
// for now flownode just use round robin selector
|
// for now flownode just use round-robin selector
|
||||||
let flow_selector = RoundRobinSelector::new(SelectTarget::Flownode);
|
let flow_selector = RoundRobinSelector::new(SelectTarget::Flownode);
|
||||||
let flow_selector_ctx = selector_ctx.clone();
|
let flow_selector_ctx = selector_ctx.clone();
|
||||||
let peer_allocator = Arc::new(FlowPeerAllocator::new(
|
let peer_allocator = Arc::new(FlowPeerAllocator::new(
|
||||||
@@ -347,6 +347,7 @@ impl MetasrvBuilder {
|
|||||||
.with_plugins(plugins.clone())
|
.with_plugins(plugins.clone())
|
||||||
.with_region_failure_handler(region_failover_handler)
|
.with_region_failure_handler(region_failover_handler)
|
||||||
.with_region_lease_handler(Some(region_lease_handler))
|
.with_region_lease_handler(Some(region_lease_handler))
|
||||||
|
.with_flush_stats_factor(Some(options.flush_stats_factor))
|
||||||
.add_default_handlers()
|
.add_default_handlers()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ use crate::metasrv::SelectTarget;
|
|||||||
use crate::selector::SelectorOptions;
|
use crate::selector::SelectorOptions;
|
||||||
|
|
||||||
/// According to the `opts`, choose peers from the `weight_array` through `weighted_choose`.
|
/// According to the `opts`, choose peers from the `weight_array` through `weighted_choose`.
|
||||||
pub fn choose_peers<W>(opts: &SelectorOptions, weighted_choose: &mut W) -> Result<Vec<Peer>>
|
pub fn choose_items<W>(opts: &SelectorOptions, weighted_choose: &mut W) -> Result<Vec<Peer>>
|
||||||
where
|
where
|
||||||
W: WeightedChoose<Peer>,
|
W: WeightedChoose<Peer>,
|
||||||
{
|
{
|
||||||
@@ -36,20 +36,36 @@ where
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if opts.allow_duplication {
|
if min_required_items == 1 {
|
||||||
(0..min_required_items)
|
// fast path
|
||||||
.map(|_| weighted_choose.choose_one())
|
return Ok(vec![weighted_choose.choose_one()?]);
|
||||||
.collect::<Result<_>>()
|
}
|
||||||
} else {
|
|
||||||
let weight_array_len = weighted_choose.len();
|
|
||||||
|
|
||||||
// When opts.allow_duplication is false, we need to check that the length of the weighted array is greater than
|
let available_count = weighted_choose.len();
|
||||||
// or equal to min_required_items, otherwise it may cause an infinite loop.
|
|
||||||
|
if opts.allow_duplication {
|
||||||
|
// Calculate how many complete rounds of `available_count` items to select,
|
||||||
|
// plus any additional items needed after complete rounds.
|
||||||
|
let complete_batches = min_required_items / available_count;
|
||||||
|
let leftover_items = min_required_items % available_count;
|
||||||
|
if complete_batches == 0 {
|
||||||
|
return weighted_choose.choose_multiple(leftover_items);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = Vec::with_capacity(min_required_items);
|
||||||
|
for _ in 0..complete_batches {
|
||||||
|
result.extend(weighted_choose.choose_multiple(available_count)?);
|
||||||
|
}
|
||||||
|
result.extend(weighted_choose.choose_multiple(leftover_items)?);
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
} else {
|
||||||
|
// Ensure the available items are sufficient when duplication is not allowed.
|
||||||
ensure!(
|
ensure!(
|
||||||
weight_array_len >= min_required_items,
|
available_count >= min_required_items,
|
||||||
error::NoEnoughAvailableNodeSnafu {
|
error::NoEnoughAvailableNodeSnafu {
|
||||||
required: min_required_items,
|
required: min_required_items,
|
||||||
available: weight_array_len,
|
available: available_count,
|
||||||
select_target: SelectTarget::Datanode
|
select_target: SelectTarget::Datanode
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -64,7 +80,7 @@ mod tests {
|
|||||||
|
|
||||||
use common_meta::peer::Peer;
|
use common_meta::peer::Peer;
|
||||||
|
|
||||||
use crate::selector::common::choose_peers;
|
use crate::selector::common::choose_items;
|
||||||
use crate::selector::weighted_choose::{RandomWeightedChoose, WeightedItem};
|
use crate::selector::weighted_choose::{RandomWeightedChoose, WeightedItem};
|
||||||
use crate::selector::SelectorOptions;
|
use crate::selector::SelectorOptions;
|
||||||
|
|
||||||
@@ -115,7 +131,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let selected_peers: HashSet<_> =
|
let selected_peers: HashSet<_> =
|
||||||
choose_peers(&opts, &mut RandomWeightedChoose::new(weight_array.clone()))
|
choose_items(&opts, &mut RandomWeightedChoose::new(weight_array.clone()))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
@@ -129,7 +145,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let selected_result =
|
let selected_result =
|
||||||
choose_peers(&opts, &mut RandomWeightedChoose::new(weight_array.clone()));
|
choose_items(&opts, &mut RandomWeightedChoose::new(weight_array.clone()));
|
||||||
assert!(selected_result.is_err());
|
assert!(selected_result.is_err());
|
||||||
|
|
||||||
for i in 1..=50 {
|
for i in 1..=50 {
|
||||||
@@ -139,7 +155,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let selected_peers =
|
let selected_peers =
|
||||||
choose_peers(&opts, &mut RandomWeightedChoose::new(weight_array.clone())).unwrap();
|
choose_items(&opts, &mut RandomWeightedChoose::new(weight_array.clone())).unwrap();
|
||||||
|
|
||||||
assert_eq!(i, selected_peers.len());
|
assert_eq!(i, selected_peers.len());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use common_meta::peer::Peer;
|
|||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::lease;
|
use crate::lease;
|
||||||
use crate::metasrv::SelectorContext;
|
use crate::metasrv::SelectorContext;
|
||||||
use crate::selector::common::choose_peers;
|
use crate::selector::common::choose_items;
|
||||||
use crate::selector::weighted_choose::{RandomWeightedChoose, WeightedItem};
|
use crate::selector::weighted_choose::{RandomWeightedChoose, WeightedItem};
|
||||||
use crate::selector::{Namespace, Selector, SelectorOptions};
|
use crate::selector::{Namespace, Selector, SelectorOptions};
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ impl Selector for LeaseBasedSelector {
|
|||||||
|
|
||||||
// 3. choose peers by weight_array.
|
// 3. choose peers by weight_array.
|
||||||
let mut weighted_choose = RandomWeightedChoose::new(weight_array);
|
let mut weighted_choose = RandomWeightedChoose::new(weight_array);
|
||||||
let selected = choose_peers(&opts, &mut weighted_choose)?;
|
let selected = choose_items(&opts, &mut weighted_choose)?;
|
||||||
|
|
||||||
Ok(selected)
|
Ok(selected)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ use crate::error::{self, Result};
|
|||||||
use crate::key::{DatanodeLeaseKey, LeaseValue};
|
use crate::key::{DatanodeLeaseKey, LeaseValue};
|
||||||
use crate::lease;
|
use crate::lease;
|
||||||
use crate::metasrv::SelectorContext;
|
use crate::metasrv::SelectorContext;
|
||||||
use crate::selector::common::choose_peers;
|
use crate::selector::common::choose_items;
|
||||||
use crate::selector::weight_compute::{RegionNumsBasedWeightCompute, WeightCompute};
|
use crate::selector::weight_compute::{RegionNumsBasedWeightCompute, WeightCompute};
|
||||||
use crate::selector::weighted_choose::RandomWeightedChoose;
|
use crate::selector::weighted_choose::RandomWeightedChoose;
|
||||||
use crate::selector::{Namespace, Selector, SelectorOptions};
|
use crate::selector::{Namespace, Selector, SelectorOptions};
|
||||||
@@ -94,7 +94,7 @@ where
|
|||||||
|
|
||||||
// 5. choose peers by weight_array.
|
// 5. choose peers by weight_array.
|
||||||
let mut weighted_choose = RandomWeightedChoose::new(weight_array);
|
let mut weighted_choose = RandomWeightedChoose::new(weight_array);
|
||||||
let selected = choose_peers(&opts, &mut weighted_choose)?;
|
let selected = choose_items(&opts, &mut weighted_choose)?;
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"LoadBasedSelector select peers: {:?}, namespace: {}, opts: {:?}.",
|
"LoadBasedSelector select peers: {:?}, namespace: {}, opts: {:?}.",
|
||||||
|
|||||||
@@ -15,8 +15,7 @@
|
|||||||
use api::v1::SemanticType;
|
use api::v1::SemanticType;
|
||||||
use common_error::ext::ErrorExt;
|
use common_error::ext::ErrorExt;
|
||||||
use common_error::status_code::StatusCode;
|
use common_error::status_code::StatusCode;
|
||||||
use common_telemetry::info;
|
use common_telemetry::{debug, info, warn};
|
||||||
use common_telemetry::tracing::warn;
|
|
||||||
use mito2::engine::MitoEngine;
|
use mito2::engine::MitoEngine;
|
||||||
use snafu::ResultExt;
|
use snafu::ResultExt;
|
||||||
use store_api::metadata::ColumnMetadata;
|
use store_api::metadata::ColumnMetadata;
|
||||||
@@ -150,6 +149,7 @@ impl DataRegion {
|
|||||||
})
|
})
|
||||||
.collect::<Result<_>>()?;
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
|
debug!("Adding (Column id assigned) columns {new_columns:?} to region {region_id:?}");
|
||||||
// assemble alter request
|
// assemble alter request
|
||||||
let alter_request = RegionRequest::Alter(RegionAlterRequest {
|
let alter_request = RegionRequest::Alter(RegionAlterRequest {
|
||||||
schema_version: version,
|
schema_version: version,
|
||||||
|
|||||||
@@ -92,17 +92,29 @@ impl MetricEngineInner {
|
|||||||
|
|
||||||
let metadata_region_id = to_metadata_region_id(physical_region_id);
|
let metadata_region_id = to_metadata_region_id(physical_region_id);
|
||||||
let mut columns_to_add = vec![];
|
let mut columns_to_add = vec![];
|
||||||
|
// columns that already exist in physical region
|
||||||
|
let mut existing_columns = vec![];
|
||||||
|
|
||||||
|
let pre_existing_physical_columns = self
|
||||||
|
.data_region
|
||||||
|
.physical_columns(physical_region_id)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let pre_exist_cols = pre_existing_physical_columns
|
||||||
|
.iter()
|
||||||
|
.map(|col| (col.column_schema.name.as_str(), col))
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
|
||||||
|
// check pre-existing physical columns so if any columns to add is already exist,
|
||||||
|
// we can skip it in physical alter operation
|
||||||
|
// (but still need to update them in logical alter operation)
|
||||||
for col in &columns {
|
for col in &columns {
|
||||||
if self
|
if let Some(exist_column) =
|
||||||
.metadata_region
|
pre_exist_cols.get(&col.column_metadata.column_schema.name.as_str())
|
||||||
.column_semantic_type(
|
|
||||||
metadata_region_id,
|
|
||||||
logical_region_id,
|
|
||||||
&col.column_metadata.column_schema.name,
|
|
||||||
)
|
|
||||||
.await?
|
|
||||||
.is_none()
|
|
||||||
{
|
{
|
||||||
|
// push the correct column schema with correct column id
|
||||||
|
existing_columns.push(*exist_column);
|
||||||
|
} else {
|
||||||
columns_to_add.push(col.column_metadata.clone());
|
columns_to_add.push(col.column_metadata.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,16 +123,16 @@ impl MetricEngineInner {
|
|||||||
let data_region_id = to_data_region_id(physical_region_id);
|
let data_region_id = to_data_region_id(physical_region_id);
|
||||||
self.add_columns_to_physical_data_region(
|
self.add_columns_to_physical_data_region(
|
||||||
data_region_id,
|
data_region_id,
|
||||||
metadata_region_id,
|
|
||||||
logical_region_id,
|
logical_region_id,
|
||||||
columns_to_add,
|
&mut columns_to_add,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// register columns to logical region
|
// note here we don't use `columns` directly but concat `existing_columns` with `columns_to_add` to get correct metadata
|
||||||
for col in columns {
|
// about already existing columns
|
||||||
|
for metadata in existing_columns.into_iter().chain(columns_to_add.iter()) {
|
||||||
self.metadata_region
|
self.metadata_region
|
||||||
.add_column(metadata_region_id, logical_region_id, &col.column_metadata)
|
.add_column(metadata_region_id, logical_region_id, metadata)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use std::collections::{HashMap, HashSet};
|
|||||||
|
|
||||||
use api::v1::SemanticType;
|
use api::v1::SemanticType;
|
||||||
use common_error::ext::BoxedError;
|
use common_error::ext::BoxedError;
|
||||||
use common_telemetry::info;
|
use common_telemetry::{info, warn};
|
||||||
use common_time::Timestamp;
|
use common_time::Timestamp;
|
||||||
use datatypes::data_type::ConcreteDataType;
|
use datatypes::data_type::ConcreteDataType;
|
||||||
use datatypes::schema::ColumnSchema;
|
use datatypes::schema::ColumnSchema;
|
||||||
@@ -31,7 +31,6 @@ use store_api::metric_engine_consts::{
|
|||||||
METADATA_SCHEMA_KEY_COLUMN_INDEX, METADATA_SCHEMA_KEY_COLUMN_NAME,
|
METADATA_SCHEMA_KEY_COLUMN_INDEX, METADATA_SCHEMA_KEY_COLUMN_NAME,
|
||||||
METADATA_SCHEMA_TIMESTAMP_COLUMN_INDEX, METADATA_SCHEMA_TIMESTAMP_COLUMN_NAME,
|
METADATA_SCHEMA_TIMESTAMP_COLUMN_INDEX, METADATA_SCHEMA_TIMESTAMP_COLUMN_NAME,
|
||||||
METADATA_SCHEMA_VALUE_COLUMN_INDEX, METADATA_SCHEMA_VALUE_COLUMN_NAME,
|
METADATA_SCHEMA_VALUE_COLUMN_INDEX, METADATA_SCHEMA_VALUE_COLUMN_NAME,
|
||||||
PHYSICAL_TABLE_METADATA_KEY,
|
|
||||||
};
|
};
|
||||||
use store_api::mito_engine_options::{APPEND_MODE_KEY, TTL_KEY};
|
use store_api::mito_engine_options::{APPEND_MODE_KEY, TTL_KEY};
|
||||||
use store_api::region_engine::RegionEngine;
|
use store_api::region_engine::RegionEngine;
|
||||||
@@ -61,7 +60,7 @@ impl MetricEngineInner {
|
|||||||
) -> Result<AffectedRows> {
|
) -> Result<AffectedRows> {
|
||||||
Self::verify_region_create_request(&request)?;
|
Self::verify_region_create_request(&request)?;
|
||||||
|
|
||||||
let result = if request.options.contains_key(PHYSICAL_TABLE_METADATA_KEY) {
|
let result = if request.is_physical_table() {
|
||||||
self.create_physical_region(region_id, request).await
|
self.create_physical_region(region_id, request).await
|
||||||
} else if request.options.contains_key(LOGICAL_TABLE_METADATA_KEY) {
|
} else if request.options.contains_key(LOGICAL_TABLE_METADATA_KEY) {
|
||||||
let physical_region_id = self.create_logical_region(region_id, request).await?;
|
let physical_region_id = self.create_logical_region(region_id, request).await?;
|
||||||
@@ -212,11 +211,17 @@ impl MetricEngineInner {
|
|||||||
|
|
||||||
self.add_columns_to_physical_data_region(
|
self.add_columns_to_physical_data_region(
|
||||||
data_region_id,
|
data_region_id,
|
||||||
metadata_region_id,
|
|
||||||
logical_region_id,
|
logical_region_id,
|
||||||
new_columns,
|
&mut new_columns,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
// register columns to metadata region
|
||||||
|
for col in &new_columns {
|
||||||
|
self.metadata_region
|
||||||
|
.add_column(metadata_region_id, logical_region_id, col)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// register logical region to metadata region
|
// register logical region to metadata region
|
||||||
@@ -260,27 +265,24 @@ impl MetricEngineInner {
|
|||||||
Ok(data_region_id)
|
Ok(data_region_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute corresponding alter requests to mito region. New added columns' [ColumnMetadata] will be
|
/// Execute corresponding alter requests to mito region. After calling this, `new_columns` will be assign a new column id
|
||||||
/// cloned into `added_columns`.
|
/// which should be correct if the following requirements are met:
|
||||||
|
///
|
||||||
|
/// # NOTE
|
||||||
|
///
|
||||||
|
/// `new_columns` MUST NOT pre-exist in the physical region. Or the results will be wrong column id for the new columns.
|
||||||
|
///
|
||||||
pub(crate) async fn add_columns_to_physical_data_region(
|
pub(crate) async fn add_columns_to_physical_data_region(
|
||||||
&self,
|
&self,
|
||||||
data_region_id: RegionId,
|
data_region_id: RegionId,
|
||||||
metadata_region_id: RegionId,
|
|
||||||
logical_region_id: RegionId,
|
logical_region_id: RegionId,
|
||||||
mut new_columns: Vec<ColumnMetadata>,
|
new_columns: &mut [ColumnMetadata],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// alter data region
|
// alter data region
|
||||||
self.data_region
|
self.data_region
|
||||||
.add_columns(data_region_id, &mut new_columns)
|
.add_columns(data_region_id, new_columns)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// register columns to metadata region
|
|
||||||
for col in &new_columns {
|
|
||||||
self.metadata_region
|
|
||||||
.add_column(metadata_region_id, logical_region_id, col)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// safety: previous step has checked this
|
// safety: previous step has checked this
|
||||||
self.state.write().unwrap().add_physical_columns(
|
self.state.write().unwrap().add_physical_columns(
|
||||||
data_region_id,
|
data_region_id,
|
||||||
@@ -291,6 +293,34 @@ impl MetricEngineInner {
|
|||||||
info!("Create region {logical_region_id} leads to adding columns {new_columns:?} to physical region {data_region_id}");
|
info!("Create region {logical_region_id} leads to adding columns {new_columns:?} to physical region {data_region_id}");
|
||||||
PHYSICAL_COLUMN_COUNT.add(new_columns.len() as _);
|
PHYSICAL_COLUMN_COUNT.add(new_columns.len() as _);
|
||||||
|
|
||||||
|
// correct the column id
|
||||||
|
let after_alter_physical_schema = self.data_region.physical_columns(data_region_id).await?;
|
||||||
|
let after_alter_physical_schema_map = after_alter_physical_schema
|
||||||
|
.iter()
|
||||||
|
.map(|metadata| (metadata.column_schema.name.as_str(), metadata))
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
|
||||||
|
// double check to make sure column ids are not mismatched
|
||||||
|
// shouldn't be a expensive operation, given it only query for physical columns
|
||||||
|
for col in new_columns.iter_mut() {
|
||||||
|
let column_metadata = after_alter_physical_schema_map
|
||||||
|
.get(&col.column_schema.name.as_str())
|
||||||
|
.with_context(|| ColumnNotFoundSnafu {
|
||||||
|
name: &col.column_schema.name,
|
||||||
|
region_id: data_region_id,
|
||||||
|
})?;
|
||||||
|
if col != *column_metadata {
|
||||||
|
warn!(
|
||||||
|
"Add already existing columns with different column metadata to physical region({:?}): new column={:?}, old column={:?}",
|
||||||
|
data_region_id,
|
||||||
|
col,
|
||||||
|
column_metadata
|
||||||
|
);
|
||||||
|
// update to correct metadata
|
||||||
|
*col = (*column_metadata).clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,12 +354,11 @@ impl MetricEngineInner {
|
|||||||
|
|
||||||
// check if required table option is present
|
// check if required table option is present
|
||||||
ensure!(
|
ensure!(
|
||||||
request.options.contains_key(PHYSICAL_TABLE_METADATA_KEY)
|
request.is_physical_table() || request.options.contains_key(LOGICAL_TABLE_METADATA_KEY),
|
||||||
|| request.options.contains_key(LOGICAL_TABLE_METADATA_KEY),
|
|
||||||
MissingRegionOptionSnafu {}
|
MissingRegionOptionSnafu {}
|
||||||
);
|
);
|
||||||
ensure!(
|
ensure!(
|
||||||
!(request.options.contains_key(PHYSICAL_TABLE_METADATA_KEY)
|
!(request.is_physical_table()
|
||||||
&& request.options.contains_key(LOGICAL_TABLE_METADATA_KEY)),
|
&& request.options.contains_key(LOGICAL_TABLE_METADATA_KEY)),
|
||||||
ConflictRegionOptionSnafu {}
|
ConflictRegionOptionSnafu {}
|
||||||
);
|
);
|
||||||
@@ -512,7 +541,7 @@ impl MetricEngineInner {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use store_api::metric_engine_consts::METRIC_ENGINE_NAME;
|
use store_api::metric_engine_consts::{METRIC_ENGINE_NAME, PHYSICAL_TABLE_METADATA_KEY};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::engine::MetricEngine;
|
use crate::engine::MetricEngine;
|
||||||
|
|||||||
@@ -18,9 +18,7 @@ use common_telemetry::info;
|
|||||||
use mito2::engine::MITO_ENGINE_NAME;
|
use mito2::engine::MITO_ENGINE_NAME;
|
||||||
use object_store::util::join_dir;
|
use object_store::util::join_dir;
|
||||||
use snafu::ResultExt;
|
use snafu::ResultExt;
|
||||||
use store_api::metric_engine_consts::{
|
use store_api::metric_engine_consts::{DATA_REGION_SUBDIR, METADATA_REGION_SUBDIR};
|
||||||
DATA_REGION_SUBDIR, METADATA_REGION_SUBDIR, PHYSICAL_TABLE_METADATA_KEY,
|
|
||||||
};
|
|
||||||
use store_api::region_engine::RegionEngine;
|
use store_api::region_engine::RegionEngine;
|
||||||
use store_api::region_request::{AffectedRows, RegionOpenRequest, RegionRequest};
|
use store_api::region_request::{AffectedRows, RegionOpenRequest, RegionRequest};
|
||||||
use store_api::storage::RegionId;
|
use store_api::storage::RegionId;
|
||||||
@@ -46,9 +44,7 @@ impl MetricEngineInner {
|
|||||||
region_id: RegionId,
|
region_id: RegionId,
|
||||||
request: RegionOpenRequest,
|
request: RegionOpenRequest,
|
||||||
) -> Result<AffectedRows> {
|
) -> Result<AffectedRows> {
|
||||||
let is_opening_physical_region = request.options.contains_key(PHYSICAL_TABLE_METADATA_KEY);
|
if request.is_physical_table() {
|
||||||
|
|
||||||
if is_opening_physical_region {
|
|
||||||
// open physical region and recover states
|
// open physical region and recover states
|
||||||
self.open_physical_region(region_id, request).await?;
|
self.open_physical_region(region_id, request).await?;
|
||||||
self.recover_states(region_id).await?;
|
self.recover_states(region_id).await?;
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
//! Implementation of retrieving logical region's region metadata.
|
//! Implementation of retrieving logical region's region metadata.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use store_api::metadata::ColumnMetadata;
|
use store_api::metadata::ColumnMetadata;
|
||||||
use store_api::storage::RegionId;
|
use store_api::storage::RegionId;
|
||||||
|
|
||||||
@@ -46,23 +48,36 @@ impl MetricEngineInner {
|
|||||||
.read_lock_logical_region(logical_region_id)
|
.read_lock_logical_region(logical_region_id)
|
||||||
.await;
|
.await;
|
||||||
// Load logical and physical columns, and intersect them to get logical column metadata.
|
// Load logical and physical columns, and intersect them to get logical column metadata.
|
||||||
let mut logical_column_metadata = self
|
let logical_column_metadata = self
|
||||||
.metadata_region
|
.metadata_region
|
||||||
.logical_columns(physical_region_id, logical_region_id)
|
.logical_columns(physical_region_id, logical_region_id)
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(_, column_metadata)| column_metadata)
|
.map(|(_, column_metadata)| column_metadata)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
// Sort columns on column name to ensure the order
|
|
||||||
logical_column_metadata
|
|
||||||
.sort_unstable_by(|c1, c2| c1.column_schema.name.cmp(&c2.column_schema.name));
|
|
||||||
// Update cache
|
|
||||||
self.state
|
|
||||||
.write()
|
|
||||||
.unwrap()
|
|
||||||
.add_logical_columns(logical_region_id, logical_column_metadata.clone());
|
|
||||||
|
|
||||||
Ok(logical_column_metadata)
|
// Update cache
|
||||||
|
let mut mutable_state = self.state.write().unwrap();
|
||||||
|
// Merge with existing cached columns.
|
||||||
|
let existing_columns = mutable_state
|
||||||
|
.logical_columns()
|
||||||
|
.get(&logical_region_id)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter();
|
||||||
|
let mut dedup_columns = logical_column_metadata
|
||||||
|
.into_iter()
|
||||||
|
.chain(existing_columns)
|
||||||
|
.map(|c| (c.column_id, c))
|
||||||
|
.collect::<HashMap<_, _>>()
|
||||||
|
.values()
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
// Sort columns on column name to ensure the order
|
||||||
|
dedup_columns.sort_unstable_by(|c1, c2| c1.column_schema.name.cmp(&c2.column_schema.name));
|
||||||
|
mutable_state.set_logical_columns(logical_region_id, dedup_columns.clone());
|
||||||
|
|
||||||
|
Ok(dedup_columns)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load logical column names of a logical region.
|
/// Load logical column names of a logical region.
|
||||||
|
|||||||
@@ -85,19 +85,13 @@ impl MetricEngineState {
|
|||||||
.insert(logical_region_id, physical_region_id);
|
.insert(logical_region_id, physical_region_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add and reorder logical columns.
|
/// Replace the logical columns of the logical region with given columns.
|
||||||
///
|
pub fn set_logical_columns(
|
||||||
/// Caller should make sure:
|
|
||||||
/// 1. there is no duplicate columns
|
|
||||||
/// 2. the column order is the same with the order in the metadata, which is
|
|
||||||
/// alphabetically ordered on column name.
|
|
||||||
pub fn add_logical_columns(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
logical_region_id: RegionId,
|
logical_region_id: RegionId,
|
||||||
new_columns: impl IntoIterator<Item = ColumnMetadata>,
|
columns: Vec<ColumnMetadata>,
|
||||||
) {
|
) {
|
||||||
let columns = self.logical_columns.entry(logical_region_id).or_default();
|
self.logical_columns.insert(logical_region_id, columns);
|
||||||
columns.extend(new_columns);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_physical_region_id(&self, logical_region_id: RegionId) -> Option<RegionId> {
|
pub fn get_physical_region_id(&self, logical_region_id: RegionId) -> Option<RegionId> {
|
||||||
|
|||||||
@@ -211,6 +211,7 @@ impl MetadataRegion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the given column exists. Return the semantic type if exists.
|
/// Check if the given column exists. Return the semantic type if exists.
|
||||||
|
#[cfg(test)]
|
||||||
pub async fn column_semantic_type(
|
pub async fn column_semantic_type(
|
||||||
&self,
|
&self,
|
||||||
physical_region_id: RegionId,
|
physical_region_id: RegionId,
|
||||||
@@ -373,6 +374,7 @@ impl MetadataRegion {
|
|||||||
|
|
||||||
/// Retrieves the value associated with the given key in the specified region.
|
/// Retrieves the value associated with the given key in the specified region.
|
||||||
/// Returns `Ok(None)` if the key is not found.
|
/// Returns `Ok(None)` if the key is not found.
|
||||||
|
#[cfg(test)]
|
||||||
pub async fn get(&self, region_id: RegionId, key: &str) -> Result<Option<String>> {
|
pub async fn get(&self, region_id: RegionId, key: &str) -> Result<Option<String>> {
|
||||||
let scan_req = Self::build_read_request(key);
|
let scan_req = Self::build_read_request(key);
|
||||||
let record_batch_stream = self
|
let record_batch_stream = self
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ common-datasource.workspace = true
|
|||||||
common-decimal.workspace = true
|
common-decimal.workspace = true
|
||||||
common-error.workspace = true
|
common-error.workspace = true
|
||||||
common-macro.workspace = true
|
common-macro.workspace = true
|
||||||
|
common-meta.workspace = true
|
||||||
common-query.workspace = true
|
common-query.workspace = true
|
||||||
common-recordbatch.workspace = true
|
common-recordbatch.workspace = true
|
||||||
common-runtime.workspace = true
|
common-runtime.workspace = true
|
||||||
@@ -74,6 +75,7 @@ uuid.workspace = true
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
common-function.workspace = true
|
common-function.workspace = true
|
||||||
|
common-meta = { workspace = true, features = ["testing"] }
|
||||||
common-procedure-test.workspace = true
|
common-procedure-test.workspace = true
|
||||||
common-test-util.workspace = true
|
common-test-util.workspace = true
|
||||||
criterion = "0.4"
|
criterion = "0.4"
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ use std::time::{Duration, Instant};
|
|||||||
|
|
||||||
use api::v1::region::compact_request;
|
use api::v1::region::compact_request;
|
||||||
use common_base::Plugins;
|
use common_base::Plugins;
|
||||||
use common_telemetry::{debug, error, info};
|
use common_meta::key::SchemaMetadataManagerRef;
|
||||||
|
use common_telemetry::{debug, error, info, warn};
|
||||||
use common_time::range::TimestampRange;
|
use common_time::range::TimestampRange;
|
||||||
use common_time::timestamp::TimeUnit;
|
use common_time::timestamp::TimeUnit;
|
||||||
use common_time::Timestamp;
|
use common_time::Timestamp;
|
||||||
@@ -37,7 +38,7 @@ use datafusion_expr::Expr;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use snafu::{OptionExt, ResultExt};
|
use snafu::{OptionExt, ResultExt};
|
||||||
use store_api::metadata::RegionMetadataRef;
|
use store_api::metadata::RegionMetadataRef;
|
||||||
use store_api::storage::RegionId;
|
use store_api::storage::{RegionId, TableId};
|
||||||
use table::predicate::Predicate;
|
use table::predicate::Predicate;
|
||||||
use tokio::sync::mpsc::{self, Sender};
|
use tokio::sync::mpsc::{self, Sender};
|
||||||
|
|
||||||
@@ -48,8 +49,8 @@ use crate::compaction::picker::{new_picker, CompactionTask};
|
|||||||
use crate::compaction::task::CompactionTaskImpl;
|
use crate::compaction::task::CompactionTaskImpl;
|
||||||
use crate::config::MitoConfig;
|
use crate::config::MitoConfig;
|
||||||
use crate::error::{
|
use crate::error::{
|
||||||
CompactRegionSnafu, Error, RegionClosedSnafu, RegionDroppedSnafu, RegionTruncatedSnafu,
|
CompactRegionSnafu, Error, GetSchemaMetadataSnafu, RegionClosedSnafu, RegionDroppedSnafu,
|
||||||
RemoteCompactionSnafu, Result, TimeRangePredicateOverflowSnafu,
|
RegionTruncatedSnafu, RemoteCompactionSnafu, Result, TimeRangePredicateOverflowSnafu,
|
||||||
};
|
};
|
||||||
use crate::metrics::COMPACTION_STAGE_ELAPSED;
|
use crate::metrics::COMPACTION_STAGE_ELAPSED;
|
||||||
use crate::read::projection::ProjectionMapper;
|
use crate::read::projection::ProjectionMapper;
|
||||||
@@ -82,6 +83,7 @@ pub struct CompactionRequest {
|
|||||||
pub(crate) cache_manager: CacheManagerRef,
|
pub(crate) cache_manager: CacheManagerRef,
|
||||||
pub(crate) manifest_ctx: ManifestContextRef,
|
pub(crate) manifest_ctx: ManifestContextRef,
|
||||||
pub(crate) listener: WorkerListener,
|
pub(crate) listener: WorkerListener,
|
||||||
|
pub(crate) schema_metadata_manager: SchemaMetadataManagerRef,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompactionRequest {
|
impl CompactionRequest {
|
||||||
@@ -141,6 +143,7 @@ impl CompactionScheduler {
|
|||||||
access_layer: &AccessLayerRef,
|
access_layer: &AccessLayerRef,
|
||||||
waiter: OptionOutputTx,
|
waiter: OptionOutputTx,
|
||||||
manifest_ctx: &ManifestContextRef,
|
manifest_ctx: &ManifestContextRef,
|
||||||
|
schema_metadata_manager: SchemaMetadataManagerRef,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if let Some(status) = self.region_status.get_mut(®ion_id) {
|
if let Some(status) = self.region_status.get_mut(®ion_id) {
|
||||||
// Region is compacting. Add the waiter to pending list.
|
// Region is compacting. Add the waiter to pending list.
|
||||||
@@ -158,6 +161,7 @@ impl CompactionScheduler {
|
|||||||
self.cache_manager.clone(),
|
self.cache_manager.clone(),
|
||||||
manifest_ctx,
|
manifest_ctx,
|
||||||
self.listener.clone(),
|
self.listener.clone(),
|
||||||
|
schema_metadata_manager,
|
||||||
);
|
);
|
||||||
self.region_status.insert(region_id, status);
|
self.region_status.insert(region_id, status);
|
||||||
let result = self
|
let result = self
|
||||||
@@ -173,6 +177,7 @@ impl CompactionScheduler {
|
|||||||
&mut self,
|
&mut self,
|
||||||
region_id: RegionId,
|
region_id: RegionId,
|
||||||
manifest_ctx: &ManifestContextRef,
|
manifest_ctx: &ManifestContextRef,
|
||||||
|
schema_metadata_manager: SchemaMetadataManagerRef,
|
||||||
) {
|
) {
|
||||||
let Some(status) = self.region_status.get_mut(®ion_id) else {
|
let Some(status) = self.region_status.get_mut(®ion_id) else {
|
||||||
return;
|
return;
|
||||||
@@ -186,6 +191,7 @@ impl CompactionScheduler {
|
|||||||
self.cache_manager.clone(),
|
self.cache_manager.clone(),
|
||||||
manifest_ctx,
|
manifest_ctx,
|
||||||
self.listener.clone(),
|
self.listener.clone(),
|
||||||
|
schema_metadata_manager,
|
||||||
);
|
);
|
||||||
// Try to schedule next compaction task for this region.
|
// Try to schedule next compaction task for this region.
|
||||||
if let Err(e) = self
|
if let Err(e) = self
|
||||||
@@ -256,10 +262,23 @@ impl CompactionScheduler {
|
|||||||
cache_manager,
|
cache_manager,
|
||||||
manifest_ctx,
|
manifest_ctx,
|
||||||
listener,
|
listener,
|
||||||
|
schema_metadata_manager,
|
||||||
} = request;
|
} = request;
|
||||||
|
|
||||||
|
let ttl = find_ttl(
|
||||||
|
region_id.table_id(),
|
||||||
|
current_version.options.ttl,
|
||||||
|
&schema_metadata_manager,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
warn!(e; "Failed to get ttl for region: {}", region_id);
|
||||||
|
None
|
||||||
|
});
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"Pick compaction strategy {:?} for region: {}",
|
"Pick compaction strategy {:?} for region: {}, ttl: {:?}",
|
||||||
picker, region_id
|
picker, region_id, ttl
|
||||||
);
|
);
|
||||||
|
|
||||||
let compaction_region = CompactionRegion {
|
let compaction_region = CompactionRegion {
|
||||||
@@ -273,6 +292,7 @@ impl CompactionScheduler {
|
|||||||
access_layer: access_layer.clone(),
|
access_layer: access_layer.clone(),
|
||||||
manifest_ctx: manifest_ctx.clone(),
|
manifest_ctx: manifest_ctx.clone(),
|
||||||
file_purger: None,
|
file_purger: None,
|
||||||
|
ttl,
|
||||||
};
|
};
|
||||||
|
|
||||||
let picker_output = {
|
let picker_output = {
|
||||||
@@ -414,6 +434,24 @@ impl PendingCompaction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finds TTL of table by first examine table options then database options.
|
||||||
|
async fn find_ttl(
|
||||||
|
table_id: TableId,
|
||||||
|
table_ttl: Option<Duration>,
|
||||||
|
schema_metadata_manager: &SchemaMetadataManagerRef,
|
||||||
|
) -> Result<Option<Duration>> {
|
||||||
|
if let Some(table_ttl) = table_ttl {
|
||||||
|
return Ok(Some(table_ttl));
|
||||||
|
}
|
||||||
|
|
||||||
|
let ttl = schema_metadata_manager
|
||||||
|
.get_schema_options_by_table_id(table_id)
|
||||||
|
.await
|
||||||
|
.context(GetSchemaMetadataSnafu)?
|
||||||
|
.and_then(|options| options.ttl);
|
||||||
|
Ok(ttl)
|
||||||
|
}
|
||||||
|
|
||||||
/// Status of running and pending region compaction tasks.
|
/// Status of running and pending region compaction tasks.
|
||||||
struct CompactionStatus {
|
struct CompactionStatus {
|
||||||
/// Id of the region.
|
/// Id of the region.
|
||||||
@@ -471,6 +509,7 @@ impl CompactionStatus {
|
|||||||
cache_manager: CacheManagerRef,
|
cache_manager: CacheManagerRef,
|
||||||
manifest_ctx: &ManifestContextRef,
|
manifest_ctx: &ManifestContextRef,
|
||||||
listener: WorkerListener,
|
listener: WorkerListener,
|
||||||
|
schema_metadata_manager: SchemaMetadataManagerRef,
|
||||||
) -> CompactionRequest {
|
) -> CompactionRequest {
|
||||||
let current_version = self.version_control.current().version;
|
let current_version = self.version_control.current().version;
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
@@ -484,6 +523,7 @@ impl CompactionStatus {
|
|||||||
cache_manager,
|
cache_manager,
|
||||||
manifest_ctx: manifest_ctx.clone(),
|
manifest_ctx: manifest_ctx.clone(),
|
||||||
listener,
|
listener,
|
||||||
|
schema_metadata_manager,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(pending) = self.pending_compaction.take() {
|
if let Some(pending) = self.pending_compaction.take() {
|
||||||
@@ -639,6 +679,9 @@ fn get_expired_ssts(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use common_meta::key::SchemaMetadataManager;
|
||||||
|
use common_meta::kv_backend::memory::MemoryKvBackend;
|
||||||
|
use common_meta::kv_backend::KvBackendRef;
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -651,7 +694,19 @@ mod tests {
|
|||||||
let (tx, _rx) = mpsc::channel(4);
|
let (tx, _rx) = mpsc::channel(4);
|
||||||
let mut scheduler = env.mock_compaction_scheduler(tx);
|
let mut scheduler = env.mock_compaction_scheduler(tx);
|
||||||
let mut builder = VersionControlBuilder::new();
|
let mut builder = VersionControlBuilder::new();
|
||||||
|
let schema_metadata_manager = Arc::new(SchemaMetadataManager::new(Arc::new(
|
||||||
|
MemoryKvBackend::new(),
|
||||||
|
)
|
||||||
|
as KvBackendRef));
|
||||||
|
schema_metadata_manager
|
||||||
|
.register_region_table_info(
|
||||||
|
builder.region_id().table_id(),
|
||||||
|
"test_table",
|
||||||
|
"test_catalog",
|
||||||
|
"test_schema",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
// Nothing to compact.
|
// Nothing to compact.
|
||||||
let version_control = Arc::new(builder.build());
|
let version_control = Arc::new(builder.build());
|
||||||
let (output_tx, output_rx) = oneshot::channel();
|
let (output_tx, output_rx) = oneshot::channel();
|
||||||
@@ -667,6 +722,7 @@ mod tests {
|
|||||||
&env.access_layer,
|
&env.access_layer,
|
||||||
waiter,
|
waiter,
|
||||||
&manifest_ctx,
|
&manifest_ctx,
|
||||||
|
schema_metadata_manager.clone(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -686,6 +742,7 @@ mod tests {
|
|||||||
&env.access_layer,
|
&env.access_layer,
|
||||||
waiter,
|
waiter,
|
||||||
&manifest_ctx,
|
&manifest_ctx,
|
||||||
|
schema_metadata_manager,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -703,6 +760,19 @@ mod tests {
|
|||||||
let mut builder = VersionControlBuilder::new();
|
let mut builder = VersionControlBuilder::new();
|
||||||
let purger = builder.file_purger();
|
let purger = builder.file_purger();
|
||||||
let region_id = builder.region_id();
|
let region_id = builder.region_id();
|
||||||
|
let schema_metadata_manager = Arc::new(SchemaMetadataManager::new(Arc::new(
|
||||||
|
MemoryKvBackend::new(),
|
||||||
|
)
|
||||||
|
as KvBackendRef));
|
||||||
|
schema_metadata_manager
|
||||||
|
.register_region_table_info(
|
||||||
|
builder.region_id().table_id(),
|
||||||
|
"test_table",
|
||||||
|
"test_catalog",
|
||||||
|
"test_schema",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
// 5 files to compact.
|
// 5 files to compact.
|
||||||
let end = 1000 * 1000;
|
let end = 1000 * 1000;
|
||||||
@@ -726,6 +796,7 @@ mod tests {
|
|||||||
&env.access_layer,
|
&env.access_layer,
|
||||||
OptionOutputTx::none(),
|
OptionOutputTx::none(),
|
||||||
&manifest_ctx,
|
&manifest_ctx,
|
||||||
|
schema_metadata_manager.clone(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -755,6 +826,7 @@ mod tests {
|
|||||||
&env.access_layer,
|
&env.access_layer,
|
||||||
OptionOutputTx::none(),
|
OptionOutputTx::none(),
|
||||||
&manifest_ctx,
|
&manifest_ctx,
|
||||||
|
schema_metadata_manager.clone(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -769,7 +841,7 @@ mod tests {
|
|||||||
|
|
||||||
// On compaction finished and schedule next compaction.
|
// On compaction finished and schedule next compaction.
|
||||||
scheduler
|
scheduler
|
||||||
.on_compaction_finished(region_id, &manifest_ctx)
|
.on_compaction_finished(region_id, &manifest_ctx, schema_metadata_manager.clone())
|
||||||
.await;
|
.await;
|
||||||
assert_eq!(1, scheduler.region_status.len());
|
assert_eq!(1, scheduler.region_status.len());
|
||||||
assert_eq!(2, job_scheduler.num_jobs());
|
assert_eq!(2, job_scheduler.num_jobs());
|
||||||
@@ -789,6 +861,7 @@ mod tests {
|
|||||||
&env.access_layer,
|
&env.access_layer,
|
||||||
OptionOutputTx::none(),
|
OptionOutputTx::none(),
|
||||||
&manifest_ctx,
|
&manifest_ctx,
|
||||||
|
schema_metadata_manager,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ use std::sync::Arc;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use api::v1::region::compact_request;
|
use api::v1::region::compact_request;
|
||||||
use common_telemetry::info;
|
use common_meta::key::SchemaMetadataManagerRef;
|
||||||
|
use common_telemetry::{info, warn};
|
||||||
use object_store::manager::ObjectStoreManagerRef;
|
use object_store::manager::ObjectStoreManagerRef;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
@@ -27,7 +28,7 @@ use store_api::storage::RegionId;
|
|||||||
use crate::access_layer::{AccessLayer, AccessLayerRef, OperationType, SstWriteRequest};
|
use crate::access_layer::{AccessLayer, AccessLayerRef, OperationType, SstWriteRequest};
|
||||||
use crate::cache::{CacheManager, CacheManagerRef};
|
use crate::cache::{CacheManager, CacheManagerRef};
|
||||||
use crate::compaction::picker::{new_picker, PickerOutput};
|
use crate::compaction::picker::{new_picker, PickerOutput};
|
||||||
use crate::compaction::CompactionSstReaderBuilder;
|
use crate::compaction::{find_ttl, CompactionSstReaderBuilder};
|
||||||
use crate::config::MitoConfig;
|
use crate::config::MitoConfig;
|
||||||
use crate::error::{EmptyRegionDirSnafu, JoinSnafu, ObjectStoreNotFoundSnafu, Result};
|
use crate::error::{EmptyRegionDirSnafu, JoinSnafu, ObjectStoreNotFoundSnafu, Result};
|
||||||
use crate::manifest::action::{RegionEdit, RegionMetaAction, RegionMetaActionList};
|
use crate::manifest::action::{RegionEdit, RegionMetaAction, RegionMetaActionList};
|
||||||
@@ -62,6 +63,7 @@ pub struct CompactionRegion {
|
|||||||
pub(crate) manifest_ctx: Arc<ManifestContext>,
|
pub(crate) manifest_ctx: Arc<ManifestContext>,
|
||||||
pub(crate) current_version: VersionRef,
|
pub(crate) current_version: VersionRef,
|
||||||
pub(crate) file_purger: Option<Arc<LocalFilePurger>>,
|
pub(crate) file_purger: Option<Arc<LocalFilePurger>>,
|
||||||
|
pub(crate) ttl: Option<Duration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// OpenCompactionRegionRequest represents the request to open a compaction region.
|
/// OpenCompactionRegionRequest represents the request to open a compaction region.
|
||||||
@@ -78,6 +80,7 @@ pub async fn open_compaction_region(
|
|||||||
req: &OpenCompactionRegionRequest,
|
req: &OpenCompactionRegionRequest,
|
||||||
mito_config: &MitoConfig,
|
mito_config: &MitoConfig,
|
||||||
object_store_manager: ObjectStoreManagerRef,
|
object_store_manager: ObjectStoreManagerRef,
|
||||||
|
schema_metadata_manager: SchemaMetadataManagerRef,
|
||||||
) -> Result<CompactionRegion> {
|
) -> Result<CompactionRegion> {
|
||||||
let object_store = {
|
let object_store = {
|
||||||
let name = &req.region_options.storage;
|
let name = &req.region_options.storage;
|
||||||
@@ -169,6 +172,16 @@ pub async fn open_compaction_region(
|
|||||||
Arc::new(version)
|
Arc::new(version)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let ttl = find_ttl(
|
||||||
|
req.region_id.table_id(),
|
||||||
|
current_version.options.ttl,
|
||||||
|
&schema_metadata_manager,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
warn!(e; "Failed to get ttl for region: {}", region_metadata.region_id);
|
||||||
|
None
|
||||||
|
});
|
||||||
Ok(CompactionRegion {
|
Ok(CompactionRegion {
|
||||||
region_id: req.region_id,
|
region_id: req.region_id,
|
||||||
region_options: req.region_options.clone(),
|
region_options: req.region_options.clone(),
|
||||||
@@ -180,6 +193,7 @@ pub async fn open_compaction_region(
|
|||||||
manifest_ctx,
|
manifest_ctx,
|
||||||
current_version,
|
current_version,
|
||||||
file_purger: Some(file_purger),
|
file_purger: Some(file_purger),
|
||||||
|
ttl,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -212,8 +212,9 @@ impl Picker for TwcsPicker {
|
|||||||
fn pick(&self, compaction_region: &CompactionRegion) -> Option<PickerOutput> {
|
fn pick(&self, compaction_region: &CompactionRegion) -> Option<PickerOutput> {
|
||||||
let region_id = compaction_region.region_id;
|
let region_id = compaction_region.region_id;
|
||||||
let levels = compaction_region.current_version.ssts.levels();
|
let levels = compaction_region.current_version.ssts.levels();
|
||||||
let ttl = compaction_region.current_version.options.ttl;
|
|
||||||
let expired_ssts = get_expired_ssts(levels, ttl, Timestamp::current_millis());
|
let expired_ssts =
|
||||||
|
get_expired_ssts(levels, compaction_region.ttl, Timestamp::current_millis());
|
||||||
if !expired_ssts.is_empty() {
|
if !expired_ssts.is_empty() {
|
||||||
info!("Expired SSTs in region {}: {:?}", region_id, expired_ssts);
|
info!("Expired SSTs in region {}: {:?}", region_id, expired_ssts);
|
||||||
// here we mark expired SSTs as compacting to avoid them being picked.
|
// here we mark expired SSTs as compacting to avoid them being picked.
|
||||||
@@ -297,6 +298,9 @@ fn assign_to_windows<'a>(
|
|||||||
let mut windows: HashMap<i64, Window> = HashMap::new();
|
let mut windows: HashMap<i64, Window> = HashMap::new();
|
||||||
// Iterates all files and assign to time windows according to max timestamp
|
// Iterates all files and assign to time windows according to max timestamp
|
||||||
for f in files {
|
for f in files {
|
||||||
|
if f.compacting() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let (_, end) = f.time_range();
|
let (_, end) = f.time_range();
|
||||||
let time_window = end
|
let time_window = end
|
||||||
.convert_to(TimeUnit::Second)
|
.convert_to(TimeUnit::Second)
|
||||||
@@ -443,6 +447,21 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_assign_compacting_to_windows() {
|
||||||
|
let files = [
|
||||||
|
new_file_handle(FileId::random(), 0, 999, 0),
|
||||||
|
new_file_handle(FileId::random(), 0, 999, 0),
|
||||||
|
new_file_handle(FileId::random(), 0, 999, 0),
|
||||||
|
new_file_handle(FileId::random(), 0, 999, 0),
|
||||||
|
new_file_handle(FileId::random(), 0, 999, 0),
|
||||||
|
];
|
||||||
|
files[0].set_compacting(true);
|
||||||
|
files[2].set_compacting(true);
|
||||||
|
let windows = assign_to_windows(files.iter(), 3);
|
||||||
|
assert_eq!(3, windows.get(&0).unwrap().files.len());
|
||||||
|
}
|
||||||
|
|
||||||
/// (Window value, overlapping, files' time ranges in window)
|
/// (Window value, overlapping, files' time ranges in window)
|
||||||
type ExpectedWindowSpec = (i64, bool, Vec<(i64, i64)>);
|
type ExpectedWindowSpec = (i64, bool, Vec<(i64, i64)>);
|
||||||
|
|
||||||
|
|||||||
@@ -352,6 +352,28 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_assign_compacting_files_to_windows() {
|
||||||
|
let picker = WindowedCompactionPicker::new(Some(HOUR / 1000));
|
||||||
|
let files = vec![
|
||||||
|
(FileId::random(), 0, 2 * HOUR - 1, 0),
|
||||||
|
(FileId::random(), HOUR, HOUR * 3 - 1, 0),
|
||||||
|
];
|
||||||
|
let version = build_version(&files, Some(Duration::from_millis(3 * HOUR as u64)));
|
||||||
|
version.ssts.levels()[0]
|
||||||
|
.files()
|
||||||
|
.for_each(|f| f.set_compacting(true));
|
||||||
|
let (outputs, expired_ssts, window_seconds) = picker.pick_inner(
|
||||||
|
RegionId::new(0, 0),
|
||||||
|
&version,
|
||||||
|
Timestamp::new_millisecond(HOUR * 3),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(expired_ssts.is_empty());
|
||||||
|
assert_eq!(HOUR / 1000, window_seconds);
|
||||||
|
assert!(outputs.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_file_time_bucket_span() {
|
fn test_file_time_bucket_span() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ impl Default for MitoConfig {
|
|||||||
selector_result_cache_size: ReadableSize::mb(512),
|
selector_result_cache_size: ReadableSize::mb(512),
|
||||||
enable_experimental_write_cache: false,
|
enable_experimental_write_cache: false,
|
||||||
experimental_write_cache_path: String::new(),
|
experimental_write_cache_path: String::new(),
|
||||||
experimental_write_cache_size: ReadableSize::mb(512),
|
experimental_write_cache_size: ReadableSize::gb(1),
|
||||||
experimental_write_cache_ttl: None,
|
experimental_write_cache_ttl: None,
|
||||||
sst_write_buffer_size: DEFAULT_WRITE_BUFFER_SIZE,
|
sst_write_buffer_size: DEFAULT_WRITE_BUFFER_SIZE,
|
||||||
scan_parallelism: divide_num_cpus(4),
|
scan_parallelism: divide_num_cpus(4),
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ use api::region::RegionResponse;
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use common_base::Plugins;
|
use common_base::Plugins;
|
||||||
use common_error::ext::BoxedError;
|
use common_error::ext::BoxedError;
|
||||||
|
use common_meta::key::SchemaMetadataManagerRef;
|
||||||
use common_recordbatch::SendableRecordBatchStream;
|
use common_recordbatch::SendableRecordBatchStream;
|
||||||
use common_telemetry::tracing;
|
use common_telemetry::tracing;
|
||||||
use common_wal::options::{WalOptions, WAL_OPTIONS_KEY};
|
use common_wal::options::{WalOptions, WAL_OPTIONS_KEY};
|
||||||
@@ -89,7 +90,7 @@ use crate::error::{
|
|||||||
};
|
};
|
||||||
use crate::manifest::action::RegionEdit;
|
use crate::manifest::action::RegionEdit;
|
||||||
use crate::metrics::HANDLE_REQUEST_ELAPSED;
|
use crate::metrics::HANDLE_REQUEST_ELAPSED;
|
||||||
use crate::read::scan_region::{ScanParallism, ScanRegion, Scanner};
|
use crate::read::scan_region::{ScanParallelism, ScanRegion, Scanner};
|
||||||
use crate::request::{RegionEditRequest, WorkerRequest};
|
use crate::request::{RegionEditRequest, WorkerRequest};
|
||||||
use crate::wal::entry_distributor::{
|
use crate::wal::entry_distributor::{
|
||||||
build_wal_entry_distributor_and_receivers, DEFAULT_ENTRY_RECEIVER_BUFFER_SIZE,
|
build_wal_entry_distributor_and_receivers, DEFAULT_ENTRY_RECEIVER_BUFFER_SIZE,
|
||||||
@@ -112,13 +113,21 @@ impl MitoEngine {
|
|||||||
mut config: MitoConfig,
|
mut config: MitoConfig,
|
||||||
log_store: Arc<S>,
|
log_store: Arc<S>,
|
||||||
object_store_manager: ObjectStoreManagerRef,
|
object_store_manager: ObjectStoreManagerRef,
|
||||||
|
schema_metadata_manager: SchemaMetadataManagerRef,
|
||||||
plugins: Plugins,
|
plugins: Plugins,
|
||||||
) -> Result<MitoEngine> {
|
) -> Result<MitoEngine> {
|
||||||
config.sanitize(data_home)?;
|
config.sanitize(data_home)?;
|
||||||
|
|
||||||
Ok(MitoEngine {
|
Ok(MitoEngine {
|
||||||
inner: Arc::new(
|
inner: Arc::new(
|
||||||
EngineInner::new(config, log_store, object_store_manager, plugins).await?,
|
EngineInner::new(
|
||||||
|
config,
|
||||||
|
log_store,
|
||||||
|
object_store_manager,
|
||||||
|
schema_metadata_manager,
|
||||||
|
plugins,
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -278,13 +287,20 @@ impl EngineInner {
|
|||||||
config: MitoConfig,
|
config: MitoConfig,
|
||||||
log_store: Arc<S>,
|
log_store: Arc<S>,
|
||||||
object_store_manager: ObjectStoreManagerRef,
|
object_store_manager: ObjectStoreManagerRef,
|
||||||
|
schema_metadata_manager: SchemaMetadataManagerRef,
|
||||||
plugins: Plugins,
|
plugins: Plugins,
|
||||||
) -> Result<EngineInner> {
|
) -> Result<EngineInner> {
|
||||||
let config = Arc::new(config);
|
let config = Arc::new(config);
|
||||||
let wal_raw_entry_reader = Arc::new(LogStoreRawEntryReader::new(log_store.clone()));
|
let wal_raw_entry_reader = Arc::new(LogStoreRawEntryReader::new(log_store.clone()));
|
||||||
Ok(EngineInner {
|
Ok(EngineInner {
|
||||||
workers: WorkerGroup::start(config.clone(), log_store, object_store_manager, plugins)
|
workers: WorkerGroup::start(
|
||||||
.await?,
|
config.clone(),
|
||||||
|
log_store,
|
||||||
|
object_store_manager,
|
||||||
|
schema_metadata_manager,
|
||||||
|
plugins,
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
config,
|
config,
|
||||||
wal_raw_entry_reader,
|
wal_raw_entry_reader,
|
||||||
})
|
})
|
||||||
@@ -417,7 +433,7 @@ impl EngineInner {
|
|||||||
let version = region.version();
|
let version = region.version();
|
||||||
// Get cache.
|
// Get cache.
|
||||||
let cache_manager = self.workers.cache_manager();
|
let cache_manager = self.workers.cache_manager();
|
||||||
let scan_parallelism = ScanParallism {
|
let scan_parallelism = ScanParallelism {
|
||||||
parallelism: self.config.scan_parallelism,
|
parallelism: self.config.scan_parallelism,
|
||||||
channel_size: self.config.parallel_scan_channel_size,
|
channel_size: self.config.parallel_scan_channel_size,
|
||||||
};
|
};
|
||||||
@@ -583,6 +599,7 @@ impl RegionEngine for MitoEngine {
|
|||||||
|
|
||||||
// Tests methods.
|
// Tests methods.
|
||||||
#[cfg(any(test, feature = "test"))]
|
#[cfg(any(test, feature = "test"))]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
impl MitoEngine {
|
impl MitoEngine {
|
||||||
/// Returns a new [MitoEngine] for tests.
|
/// Returns a new [MitoEngine] for tests.
|
||||||
pub async fn new_for_test<S: LogStore>(
|
pub async fn new_for_test<S: LogStore>(
|
||||||
@@ -593,6 +610,7 @@ impl MitoEngine {
|
|||||||
write_buffer_manager: Option<crate::flush::WriteBufferManagerRef>,
|
write_buffer_manager: Option<crate::flush::WriteBufferManagerRef>,
|
||||||
listener: Option<crate::engine::listener::EventListenerRef>,
|
listener: Option<crate::engine::listener::EventListenerRef>,
|
||||||
time_provider: crate::time_provider::TimeProviderRef,
|
time_provider: crate::time_provider::TimeProviderRef,
|
||||||
|
schema_metadata_manager: SchemaMetadataManagerRef,
|
||||||
) -> Result<MitoEngine> {
|
) -> Result<MitoEngine> {
|
||||||
config.sanitize(data_home)?;
|
config.sanitize(data_home)?;
|
||||||
|
|
||||||
@@ -606,6 +624,7 @@ impl MitoEngine {
|
|||||||
object_store_manager,
|
object_store_manager,
|
||||||
write_buffer_manager,
|
write_buffer_manager,
|
||||||
listener,
|
listener,
|
||||||
|
schema_metadata_manager,
|
||||||
time_provider,
|
time_provider,
|
||||||
)
|
)
|
||||||
.await?,
|
.await?,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ use common_error::ext::ErrorExt;
|
|||||||
use common_error::status_code::StatusCode;
|
use common_error::status_code::StatusCode;
|
||||||
use common_recordbatch::RecordBatches;
|
use common_recordbatch::RecordBatches;
|
||||||
use datatypes::prelude::ConcreteDataType;
|
use datatypes::prelude::ConcreteDataType;
|
||||||
use datatypes::schema::ColumnSchema;
|
use datatypes::schema::{ColumnSchema, FulltextAnalyzer, FulltextOptions};
|
||||||
use store_api::metadata::ColumnMetadata;
|
use store_api::metadata::ColumnMetadata;
|
||||||
use store_api::region_engine::{RegionEngine, RegionRole};
|
use store_api::region_engine::{RegionEngine, RegionRole};
|
||||||
use store_api::region_request::{
|
use store_api::region_request::{
|
||||||
@@ -31,7 +31,7 @@ use store_api::region_request::{
|
|||||||
use store_api::storage::{RegionId, ScanRequest};
|
use store_api::storage::{RegionId, ScanRequest};
|
||||||
|
|
||||||
use crate::config::MitoConfig;
|
use crate::config::MitoConfig;
|
||||||
use crate::engine::listener::AlterFlushListener;
|
use crate::engine::listener::{AlterFlushListener, NotifyRegionChangeResultListener};
|
||||||
use crate::engine::MitoEngine;
|
use crate::engine::MitoEngine;
|
||||||
use crate::test_util::{
|
use crate::test_util::{
|
||||||
build_rows, build_rows_for_key, flush_region, put_rows, rows_schema, CreateRequestBuilder,
|
build_rows, build_rows_for_key, flush_region, put_rows, rows_schema, CreateRequestBuilder,
|
||||||
@@ -68,6 +68,36 @@ fn add_tag1() -> RegionAlterRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn alter_column_fulltext_options() -> RegionAlterRequest {
|
||||||
|
RegionAlterRequest {
|
||||||
|
schema_version: 0,
|
||||||
|
kind: AlterKind::ChangeColumnFulltext {
|
||||||
|
column_name: "tag_0".to_string(),
|
||||||
|
options: FulltextOptions {
|
||||||
|
enable: true,
|
||||||
|
analyzer: FulltextAnalyzer::English,
|
||||||
|
case_sensitive: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_region_version(
|
||||||
|
engine: &MitoEngine,
|
||||||
|
region_id: RegionId,
|
||||||
|
last_entry_id: u64,
|
||||||
|
committed_sequence: u64,
|
||||||
|
flushed_entry_id: u64,
|
||||||
|
flushed_sequence: u64,
|
||||||
|
) {
|
||||||
|
let region = engine.get_region(region_id).unwrap();
|
||||||
|
let version_data = region.version_control.current();
|
||||||
|
assert_eq!(last_entry_id, version_data.last_entry_id);
|
||||||
|
assert_eq!(committed_sequence, version_data.committed_sequence);
|
||||||
|
assert_eq!(flushed_entry_id, version_data.version.flushed_entry_id);
|
||||||
|
assert_eq!(flushed_sequence, version_data.version.flushed_sequence);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_alter_region() {
|
async fn test_alter_region() {
|
||||||
common_telemetry::init_default_ut_logging();
|
common_telemetry::init_default_ut_logging();
|
||||||
@@ -78,6 +108,16 @@ async fn test_alter_region() {
|
|||||||
let region_id = RegionId::new(1, 1);
|
let region_id = RegionId::new(1, 1);
|
||||||
let request = CreateRequestBuilder::new().build();
|
let request = CreateRequestBuilder::new().build();
|
||||||
|
|
||||||
|
env.get_schema_metadata_manager()
|
||||||
|
.register_region_table_info(
|
||||||
|
region_id.table_id(),
|
||||||
|
"test_table",
|
||||||
|
"test_catalog",
|
||||||
|
"test_schema",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
let column_schemas = rows_schema(&request);
|
let column_schemas = rows_schema(&request);
|
||||||
let region_dir = request.region_dir.clone();
|
let region_dir = request.region_dir.clone();
|
||||||
engine
|
engine
|
||||||
@@ -106,15 +146,7 @@ async fn test_alter_region() {
|
|||||||
| | 2 | 2.0 | 1970-01-01T00:00:02 |
|
| | 2 | 2.0 | 1970-01-01T00:00:02 |
|
||||||
+-------+-------+---------+---------------------+";
|
+-------+-------+---------+---------------------+";
|
||||||
scan_check_after_alter(&engine, region_id, expected).await;
|
scan_check_after_alter(&engine, region_id, expected).await;
|
||||||
let check_region = |engine: &MitoEngine| {
|
check_region_version(&engine, region_id, 1, 3, 1, 3);
|
||||||
let region = engine.get_region(region_id).unwrap();
|
|
||||||
let version_data = region.version_control.current();
|
|
||||||
assert_eq!(1, version_data.last_entry_id);
|
|
||||||
assert_eq!(3, version_data.committed_sequence);
|
|
||||||
assert_eq!(1, version_data.version.flushed_entry_id);
|
|
||||||
assert_eq!(3, version_data.version.flushed_sequence);
|
|
||||||
};
|
|
||||||
check_region(&engine);
|
|
||||||
|
|
||||||
// Reopen region.
|
// Reopen region.
|
||||||
let engine = env.reopen_engine(engine, MitoConfig::default()).await;
|
let engine = env.reopen_engine(engine, MitoConfig::default()).await;
|
||||||
@@ -131,7 +163,7 @@ async fn test_alter_region() {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
scan_check_after_alter(&engine, region_id, expected).await;
|
scan_check_after_alter(&engine, region_id, expected).await;
|
||||||
check_region(&engine);
|
check_region_version(&engine, region_id, 1, 3, 1, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build rows with schema (string, f64, ts_millis, string).
|
/// Build rows with schema (string, f64, ts_millis, string).
|
||||||
@@ -167,10 +199,19 @@ fn build_rows_for_tags(
|
|||||||
async fn test_put_after_alter() {
|
async fn test_put_after_alter() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let engine = env.create_engine(MitoConfig::default()).await;
|
let engine = env.create_engine(MitoConfig::default()).await;
|
||||||
|
|
||||||
let region_id = RegionId::new(1, 1);
|
let region_id = RegionId::new(1, 1);
|
||||||
let request = CreateRequestBuilder::new().build();
|
let request = CreateRequestBuilder::new().build();
|
||||||
|
|
||||||
|
env.get_schema_metadata_manager()
|
||||||
|
.register_region_table_info(
|
||||||
|
region_id.table_id(),
|
||||||
|
"test_table",
|
||||||
|
"test_catalog",
|
||||||
|
"test_schema",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
let mut column_schemas = rows_schema(&request);
|
let mut column_schemas = rows_schema(&request);
|
||||||
let region_dir = request.region_dir.clone();
|
let region_dir = request.region_dir.clone();
|
||||||
engine
|
engine
|
||||||
@@ -266,6 +307,16 @@ async fn test_alter_region_retry() {
|
|||||||
let region_id = RegionId::new(1, 1);
|
let region_id = RegionId::new(1, 1);
|
||||||
let request = CreateRequestBuilder::new().build();
|
let request = CreateRequestBuilder::new().build();
|
||||||
|
|
||||||
|
env.get_schema_metadata_manager()
|
||||||
|
.register_region_table_info(
|
||||||
|
region_id.table_id(),
|
||||||
|
"test_table",
|
||||||
|
"test_catalog",
|
||||||
|
"test_schema",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
let column_schemas = rows_schema(&request);
|
let column_schemas = rows_schema(&request);
|
||||||
engine
|
engine
|
||||||
.handle_request(region_id, RegionRequest::Create(request))
|
.handle_request(region_id, RegionRequest::Create(request))
|
||||||
@@ -299,12 +350,7 @@ async fn test_alter_region_retry() {
|
|||||||
| | a | 1.0 | 1970-01-01T00:00:01 |
|
| | a | 1.0 | 1970-01-01T00:00:01 |
|
||||||
+-------+-------+---------+---------------------+";
|
+-------+-------+---------+---------------------+";
|
||||||
scan_check_after_alter(&engine, region_id, expected).await;
|
scan_check_after_alter(&engine, region_id, expected).await;
|
||||||
let region = engine.get_region(region_id).unwrap();
|
check_region_version(&engine, region_id, 1, 2, 1, 2);
|
||||||
let version_data = region.version_control.current();
|
|
||||||
assert_eq!(1, version_data.last_entry_id);
|
|
||||||
assert_eq!(2, version_data.committed_sequence);
|
|
||||||
assert_eq!(1, version_data.version.flushed_entry_id);
|
|
||||||
assert_eq!(2, version_data.version.flushed_sequence);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -320,6 +366,16 @@ async fn test_alter_on_flushing() {
|
|||||||
let region_id = RegionId::new(1, 1);
|
let region_id = RegionId::new(1, 1);
|
||||||
let request = CreateRequestBuilder::new().build();
|
let request = CreateRequestBuilder::new().build();
|
||||||
|
|
||||||
|
env.get_schema_metadata_manager()
|
||||||
|
.register_region_table_info(
|
||||||
|
region_id.table_id(),
|
||||||
|
"test_table",
|
||||||
|
"test_catalog",
|
||||||
|
"test_schema",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
let column_schemas = rows_schema(&request);
|
let column_schemas = rows_schema(&request);
|
||||||
engine
|
engine
|
||||||
.handle_request(region_id, RegionRequest::Create(request))
|
.handle_request(region_id, RegionRequest::Create(request))
|
||||||
@@ -399,3 +455,187 @@ async fn test_alter_on_flushing() {
|
|||||||
+-------+-------+---------+---------------------+";
|
+-------+-------+---------+---------------------+";
|
||||||
assert_eq!(expected, batches.pretty_print().unwrap());
|
assert_eq!(expected, batches.pretty_print().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_alter_column_fulltext_options() {
|
||||||
|
common_telemetry::init_default_ut_logging();
|
||||||
|
|
||||||
|
let mut env = TestEnv::new();
|
||||||
|
let listener = Arc::new(AlterFlushListener::default());
|
||||||
|
let engine = env
|
||||||
|
.create_engine_with(MitoConfig::default(), None, Some(listener.clone()))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let region_id = RegionId::new(1, 1);
|
||||||
|
let request = CreateRequestBuilder::new().build();
|
||||||
|
|
||||||
|
env.get_schema_metadata_manager()
|
||||||
|
.register_region_table_info(
|
||||||
|
region_id.table_id(),
|
||||||
|
"test_table",
|
||||||
|
"test_catalog",
|
||||||
|
"test_schema",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let column_schemas = rows_schema(&request);
|
||||||
|
let region_dir = request.region_dir.clone();
|
||||||
|
engine
|
||||||
|
.handle_request(region_id, RegionRequest::Create(request))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let rows = Rows {
|
||||||
|
schema: column_schemas,
|
||||||
|
rows: build_rows(0, 3),
|
||||||
|
};
|
||||||
|
put_rows(&engine, region_id, rows).await;
|
||||||
|
|
||||||
|
// Spawns a task to flush the engine.
|
||||||
|
let engine_cloned = engine.clone();
|
||||||
|
let flush_job = tokio::spawn(async move {
|
||||||
|
flush_region(&engine_cloned, region_id, None).await;
|
||||||
|
});
|
||||||
|
// Waits for flush begin.
|
||||||
|
listener.wait_flush_begin().await;
|
||||||
|
|
||||||
|
// Consumes the notify permit in the listener.
|
||||||
|
listener.wait_request_begin().await;
|
||||||
|
|
||||||
|
// Submits an alter request to the region. The region should add the request
|
||||||
|
// to the pending ddl request list.
|
||||||
|
let request = alter_column_fulltext_options();
|
||||||
|
let engine_cloned = engine.clone();
|
||||||
|
let alter_job = tokio::spawn(async move {
|
||||||
|
engine_cloned
|
||||||
|
.handle_request(region_id, RegionRequest::Alter(request))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
// Waits until the worker handles the alter request.
|
||||||
|
listener.wait_request_begin().await;
|
||||||
|
|
||||||
|
// Spawns two task to flush the engine. The flush scheduler should put them to the
|
||||||
|
// pending task list.
|
||||||
|
let engine_cloned = engine.clone();
|
||||||
|
let pending_flush_job = tokio::spawn(async move {
|
||||||
|
flush_region(&engine_cloned, region_id, None).await;
|
||||||
|
});
|
||||||
|
// Waits until the worker handles the flush request.
|
||||||
|
listener.wait_request_begin().await;
|
||||||
|
|
||||||
|
// Wake up flush.
|
||||||
|
listener.wake_flush();
|
||||||
|
// Wait for the flush job.
|
||||||
|
flush_job.await.unwrap();
|
||||||
|
// Wait for pending flush job.
|
||||||
|
pending_flush_job.await.unwrap();
|
||||||
|
// Wait for the write job.
|
||||||
|
alter_job.await.unwrap();
|
||||||
|
|
||||||
|
let expect_fulltext_options = FulltextOptions {
|
||||||
|
enable: true,
|
||||||
|
analyzer: FulltextAnalyzer::English,
|
||||||
|
case_sensitive: false,
|
||||||
|
};
|
||||||
|
let check_fulltext_options = |engine: &MitoEngine, expected: &FulltextOptions| {
|
||||||
|
let current_fulltext_options = engine
|
||||||
|
.get_region(region_id)
|
||||||
|
.unwrap()
|
||||||
|
.metadata()
|
||||||
|
.column_by_name("tag_0")
|
||||||
|
.unwrap()
|
||||||
|
.column_schema
|
||||||
|
.fulltext_options()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(*expected, current_fulltext_options);
|
||||||
|
};
|
||||||
|
check_fulltext_options(&engine, &expect_fulltext_options);
|
||||||
|
check_region_version(&engine, region_id, 1, 3, 1, 3);
|
||||||
|
|
||||||
|
// Reopen region.
|
||||||
|
let engine = env.reopen_engine(engine, MitoConfig::default()).await;
|
||||||
|
engine
|
||||||
|
.handle_request(
|
||||||
|
region_id,
|
||||||
|
RegionRequest::Open(RegionOpenRequest {
|
||||||
|
engine: String::new(),
|
||||||
|
region_dir,
|
||||||
|
options: HashMap::default(),
|
||||||
|
skip_wal_replay: false,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
check_fulltext_options(&engine, &expect_fulltext_options);
|
||||||
|
check_region_version(&engine, region_id, 1, 3, 1, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_write_stall_on_altering() {
|
||||||
|
common_telemetry::init_default_ut_logging();
|
||||||
|
|
||||||
|
let mut env = TestEnv::new();
|
||||||
|
let listener = Arc::new(NotifyRegionChangeResultListener::default());
|
||||||
|
let engine = env
|
||||||
|
.create_engine_with(MitoConfig::default(), None, Some(listener.clone()))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let region_id = RegionId::new(1, 1);
|
||||||
|
let request = CreateRequestBuilder::new().build();
|
||||||
|
|
||||||
|
env.get_schema_metadata_manager()
|
||||||
|
.register_region_table_info(
|
||||||
|
region_id.table_id(),
|
||||||
|
"test_table",
|
||||||
|
"test_catalog",
|
||||||
|
"test_schema",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let column_schemas = rows_schema(&request);
|
||||||
|
engine
|
||||||
|
.handle_request(region_id, RegionRequest::Create(request))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let engine_cloned = engine.clone();
|
||||||
|
let alter_job = tokio::spawn(async move {
|
||||||
|
let request = add_tag1();
|
||||||
|
engine_cloned
|
||||||
|
.handle_request(region_id, RegionRequest::Alter(request))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
let column_schemas_cloned = column_schemas.clone();
|
||||||
|
let engine_cloned = engine.clone();
|
||||||
|
let put_job = tokio::spawn(async move {
|
||||||
|
let rows = Rows {
|
||||||
|
schema: column_schemas_cloned,
|
||||||
|
rows: build_rows(0, 3),
|
||||||
|
};
|
||||||
|
put_rows(&engine_cloned, region_id, rows).await;
|
||||||
|
});
|
||||||
|
|
||||||
|
listener.wake_notify();
|
||||||
|
alter_job.await.unwrap();
|
||||||
|
put_job.await.unwrap();
|
||||||
|
|
||||||
|
let expected = "\
|
||||||
|
+-------+-------+---------+---------------------+
|
||||||
|
| tag_1 | tag_0 | field_0 | ts |
|
||||||
|
+-------+-------+---------+---------------------+
|
||||||
|
| | 0 | 0.0 | 1970-01-01T00:00:00 |
|
||||||
|
| | 1 | 1.0 | 1970-01-01T00:00:01 |
|
||||||
|
| | 2 | 2.0 | 1970-01-01T00:00:02 |
|
||||||
|
+-------+-------+---------+---------------------+";
|
||||||
|
let request = ScanRequest::default();
|
||||||
|
let scanner = engine.scanner(region_id, request).unwrap();
|
||||||
|
let stream = scanner.scan().await.unwrap();
|
||||||
|
let batches = RecordBatches::try_collect(stream).await.unwrap();
|
||||||
|
assert_eq!(expected, batches.pretty_print().unwrap());
|
||||||
|
}
|
||||||
|
|||||||
@@ -98,6 +98,16 @@ async fn test_append_mode_compaction() {
|
|||||||
.await;
|
.await;
|
||||||
let region_id = RegionId::new(1, 1);
|
let region_id = RegionId::new(1, 1);
|
||||||
|
|
||||||
|
env.get_schema_metadata_manager()
|
||||||
|
.register_region_table_info(
|
||||||
|
region_id.table_id(),
|
||||||
|
"test_table",
|
||||||
|
"test_catalog",
|
||||||
|
"test_schema",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
let request = CreateRequestBuilder::new()
|
let request = CreateRequestBuilder::new()
|
||||||
.insert_option("compaction.type", "twcs")
|
.insert_option("compaction.type", "twcs")
|
||||||
.insert_option("compaction.twcs.max_active_window_runs", "2")
|
.insert_option("compaction.twcs.max_active_window_runs", "2")
|
||||||
|
|||||||
@@ -112,6 +112,16 @@ async fn test_compaction_region() {
|
|||||||
let engine = env.create_engine(MitoConfig::default()).await;
|
let engine = env.create_engine(MitoConfig::default()).await;
|
||||||
|
|
||||||
let region_id = RegionId::new(1, 1);
|
let region_id = RegionId::new(1, 1);
|
||||||
|
env.get_schema_metadata_manager()
|
||||||
|
.register_region_table_info(
|
||||||
|
region_id.table_id(),
|
||||||
|
"test_table",
|
||||||
|
"test_catalog",
|
||||||
|
"test_schema",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
let request = CreateRequestBuilder::new()
|
let request = CreateRequestBuilder::new()
|
||||||
.insert_option("compaction.type", "twcs")
|
.insert_option("compaction.type", "twcs")
|
||||||
.insert_option("compaction.twcs.max_active_window_runs", "1")
|
.insert_option("compaction.twcs.max_active_window_runs", "1")
|
||||||
@@ -171,8 +181,18 @@ async fn test_compaction_region_with_overlapping() {
|
|||||||
common_telemetry::init_default_ut_logging();
|
common_telemetry::init_default_ut_logging();
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let engine = env.create_engine(MitoConfig::default()).await;
|
let engine = env.create_engine(MitoConfig::default()).await;
|
||||||
|
|
||||||
let region_id = RegionId::new(1, 1);
|
let region_id = RegionId::new(1, 1);
|
||||||
|
|
||||||
|
env.get_schema_metadata_manager()
|
||||||
|
.register_region_table_info(
|
||||||
|
region_id.table_id(),
|
||||||
|
"test_table",
|
||||||
|
"test_catalog",
|
||||||
|
"test_schema",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
let request = CreateRequestBuilder::new()
|
let request = CreateRequestBuilder::new()
|
||||||
.insert_option("compaction.type", "twcs")
|
.insert_option("compaction.type", "twcs")
|
||||||
.insert_option("compaction.twcs.max_active_window_runs", "2")
|
.insert_option("compaction.twcs.max_active_window_runs", "2")
|
||||||
@@ -217,6 +237,17 @@ async fn test_compaction_region_with_overlapping_delete_all() {
|
|||||||
let engine = env.create_engine(MitoConfig::default()).await;
|
let engine = env.create_engine(MitoConfig::default()).await;
|
||||||
|
|
||||||
let region_id = RegionId::new(1, 1);
|
let region_id = RegionId::new(1, 1);
|
||||||
|
|
||||||
|
env.get_schema_metadata_manager()
|
||||||
|
.register_region_table_info(
|
||||||
|
region_id.table_id(),
|
||||||
|
"test_table",
|
||||||
|
"test_catalog",
|
||||||
|
"test_schema",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
let request = CreateRequestBuilder::new()
|
let request = CreateRequestBuilder::new()
|
||||||
.insert_option("compaction.type", "twcs")
|
.insert_option("compaction.type", "twcs")
|
||||||
.insert_option("compaction.twcs.max_active_window_runs", "2")
|
.insert_option("compaction.twcs.max_active_window_runs", "2")
|
||||||
@@ -281,6 +312,16 @@ async fn test_readonly_during_compaction() {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let region_id = RegionId::new(1, 1);
|
let region_id = RegionId::new(1, 1);
|
||||||
|
env.get_schema_metadata_manager()
|
||||||
|
.register_region_table_info(
|
||||||
|
region_id.table_id(),
|
||||||
|
"test_table",
|
||||||
|
"test_catalog",
|
||||||
|
"test_schema",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
let request = CreateRequestBuilder::new()
|
let request = CreateRequestBuilder::new()
|
||||||
.insert_option("compaction.type", "twcs")
|
.insert_option("compaction.type", "twcs")
|
||||||
.insert_option("compaction.twcs.max_active_window_runs", "1")
|
.insert_option("compaction.twcs.max_active_window_runs", "1")
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use std::sync::Arc;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use api::v1::Rows;
|
use api::v1::Rows;
|
||||||
|
use common_meta::key::SchemaMetadataManager;
|
||||||
use object_store::util::join_path;
|
use object_store::util::join_path;
|
||||||
use store_api::region_engine::RegionEngine;
|
use store_api::region_engine::RegionEngine;
|
||||||
use store_api::region_request::{RegionDropRequest, RegionRequest};
|
use store_api::region_request::{RegionDropRequest, RegionRequest};
|
||||||
@@ -40,6 +41,17 @@ async fn test_engine_drop_region() {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let region_id = RegionId::new(1, 1);
|
let region_id = RegionId::new(1, 1);
|
||||||
|
|
||||||
|
env.get_schema_metadata_manager()
|
||||||
|
.register_region_table_info(
|
||||||
|
region_id.table_id(),
|
||||||
|
"test_table",
|
||||||
|
"test_catalog",
|
||||||
|
"test_schema",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
// It's okay to drop a region doesn't exist.
|
// It's okay to drop a region doesn't exist.
|
||||||
engine
|
engine
|
||||||
.handle_request(region_id, RegionRequest::Drop(RegionDropRequest {}))
|
.handle_request(region_id, RegionRequest::Drop(RegionDropRequest {}))
|
||||||
@@ -87,7 +99,12 @@ async fn test_engine_drop_region() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_engine_drop_region_for_custom_store() {
|
async fn test_engine_drop_region_for_custom_store() {
|
||||||
common_telemetry::init_default_ut_logging();
|
common_telemetry::init_default_ut_logging();
|
||||||
async fn setup(engine: &MitoEngine, region_id: RegionId, storage_name: &str) {
|
async fn setup(
|
||||||
|
engine: &MitoEngine,
|
||||||
|
schema_metadata_manager: &SchemaMetadataManager,
|
||||||
|
region_id: RegionId,
|
||||||
|
storage_name: &str,
|
||||||
|
) {
|
||||||
let request = CreateRequestBuilder::new()
|
let request = CreateRequestBuilder::new()
|
||||||
.insert_option("storage", storage_name)
|
.insert_option("storage", storage_name)
|
||||||
.region_dir(storage_name)
|
.region_dir(storage_name)
|
||||||
@@ -97,6 +114,18 @@ async fn test_engine_drop_region_for_custom_store() {
|
|||||||
.handle_request(region_id, RegionRequest::Create(request))
|
.handle_request(region_id, RegionRequest::Create(request))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let table_id = format!("test_table_{}", region_id.table_id());
|
||||||
|
schema_metadata_manager
|
||||||
|
.register_region_table_info(
|
||||||
|
region_id.table_id(),
|
||||||
|
&table_id,
|
||||||
|
"test_catalog",
|
||||||
|
"test_schema",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
let rows = Rows {
|
let rows = Rows {
|
||||||
schema: column_schema.clone(),
|
schema: column_schema.clone(),
|
||||||
rows: build_rows_for_key("a", 0, 2, 0),
|
rows: build_rows_for_key("a", 0, 2, 0),
|
||||||
@@ -114,12 +143,19 @@ async fn test_engine_drop_region_for_custom_store() {
|
|||||||
&["Gcs"],
|
&["Gcs"],
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
let schema_metadata_manager = env.get_schema_metadata_manager();
|
||||||
let object_store_manager = env.get_object_store_manager().unwrap();
|
let object_store_manager = env.get_object_store_manager().unwrap();
|
||||||
|
|
||||||
let global_region_id = RegionId::new(1, 1);
|
let global_region_id = RegionId::new(1, 1);
|
||||||
setup(&engine, global_region_id, "default").await;
|
setup(
|
||||||
|
&engine,
|
||||||
|
&schema_metadata_manager,
|
||||||
|
global_region_id,
|
||||||
|
"default",
|
||||||
|
)
|
||||||
|
.await;
|
||||||
let custom_region_id = RegionId::new(2, 1);
|
let custom_region_id = RegionId::new(2, 1);
|
||||||
setup(&engine, custom_region_id, "Gcs").await;
|
setup(&engine, &schema_metadata_manager, custom_region_id, "Gcs").await;
|
||||||
|
|
||||||
let global_region = engine.get_region(global_region_id).unwrap();
|
let global_region = engine.get_region(global_region_id).unwrap();
|
||||||
let global_region_dir = global_region.access_layer.region_dir().to_string();
|
let global_region_dir = global_region.access_layer.region_dir().to_string();
|
||||||
|
|||||||
@@ -64,6 +64,16 @@ async fn test_edit_region_schedule_compaction() {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let region_id = RegionId::new(1, 1);
|
let region_id = RegionId::new(1, 1);
|
||||||
|
|
||||||
|
env.get_schema_metadata_manager()
|
||||||
|
.register_region_table_info(
|
||||||
|
region_id.table_id(),
|
||||||
|
"test_table",
|
||||||
|
"test_catalog",
|
||||||
|
"test_schema",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
engine
|
engine
|
||||||
.handle_request(
|
.handle_request(
|
||||||
region_id,
|
region_id,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user