feat: basic types for Dataflow Framework (#3186)

* feat: basic types for Dataflow Framework

* docs: license header

* chore: add name in todo, remove deprecated code

* chore: typo

* test: linear&repr unit test

* refactor: avoid mod.rs

* feat: Relation Type

* feat: unmat funcs

* feat: simple temporal filter(sliding window)

* refactor: impl Snafu for EvalError

* feat: cast as type

* feat: temporal filter

* feat: time index in RelationType

* refactor: move EvalError to expr

* refactor: error handling for func

* chore: fmt&comment

* make EvalError pub again

* refactor: move ScalarExpr to scalar.rs

* refactor: remove mod.rs for relation

* chore: slim down PR size

* chore: license header

* chore: per review

* chore: more fix per review

* chore: even more fix per review

* chore: fmt

* chore: fmt

* feat: impl From/Into ProtoRow instead

* chore: use cast not cast_with_opt&`Key` struct

* chore: new_unchecked

* feat: `Key::subset_of` method

* chore: toml in order
This commit is contained in:
discord9
2024-01-30 15:48:22 +08:00
committed by GitHub
parent ddbd0abe3b
commit 0a9361a63c
12 changed files with 1413 additions and 1 deletions

555
Cargo.lock generated
View File

@@ -690,6 +690,18 @@ dependencies = [
"tokio",
]
[[package]]
name = "auto_impl"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "auto_ops"
version = "0.3.0"
@@ -891,6 +903,12 @@ dependencies = [
"num-traits",
]
[[package]]
name = "bimap"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7"
[[package]]
name = "bincode"
version = "1.3.3"
@@ -1142,6 +1160,26 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "c2rust-bitfields"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb34f0c0ace43530b2df7f18bc69ee0c4082158aa451ece29602f8c841e73764"
dependencies = [
"c2rust-bitfields-derive",
]
[[package]]
name = "c2rust-bitfields-derive"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dd1601a7b828ab874d890e5a895563ca8ad485bdd3d2a359f148c8b72537241"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "cactus"
version = "1.0.6"
@@ -1254,6 +1292,18 @@ dependencies = [
"libc",
]
[[package]]
name = "cc-traits"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "060303ef31ef4a522737e1b1ab68c67916f2a787bb2f4f54f383279adba962b5"
[[package]]
name = "cesu8"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cexpr"
version = "0.6.0"
@@ -1354,6 +1404,12 @@ dependencies = [
"phf_codegen",
]
[[package]]
name = "chunked_transfer"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901"
[[package]]
name = "ciborium"
version = "0.2.1"
@@ -1601,6 +1657,16 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "combine"
version = "4.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4"
dependencies = [
"bytes",
"memchr",
]
[[package]]
name = "comfy-table"
version = "7.1.0"
@@ -2462,6 +2528,12 @@ dependencies = [
"parking_lot_core 0.9.9",
]
[[package]]
name = "data-encoding"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
[[package]]
name = "datafusion"
version = "32.0.0"
@@ -2898,6 +2970,12 @@ version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "difference"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
[[package]]
name = "difflib"
version = "0.4.0"
@@ -2916,6 +2994,15 @@ dependencies = [
"subtle",
]
[[package]]
name = "dirs"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs"
version = "4.0.0"
@@ -3304,6 +3391,29 @@ dependencies = [
"num-traits",
]
[[package]]
name = "flow"
version = "0.6.0"
dependencies = [
"api",
"bimap",
"common-error",
"common-macro",
"common-meta",
"common-query",
"common-telemetry",
"common-time",
"datatypes",
"hydroflow",
"itertools 0.10.5",
"serde",
"servers",
"session",
"snafu",
"tokio",
"tonic 0.10.2",
]
[[package]]
name = "fnv"
version = "1.0.7"
@@ -3866,6 +3976,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "html-escape"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476"
dependencies = [
"utf8-width",
]
[[package]]
name = "http"
version = "0.2.11"
@@ -3938,6 +4057,103 @@ dependencies = [
"serde",
]
[[package]]
name = "hydroflow"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5129724896b4c3cf12f8e5f5af2f1d94b4c5933ae911189747025c6a5ff1346"
dependencies = [
"bincode",
"byteorder",
"bytes",
"futures",
"getrandom",
"hydroflow_datalog",
"hydroflow_lang",
"hydroflow_macro",
"instant",
"itertools 0.10.5",
"lattices",
"pusherator",
"ref-cast",
"regex",
"rustc-hash",
"sealed",
"serde",
"serde_json",
"slotmap",
"smallvec",
"tokio",
"tokio-stream",
"tokio-util",
"tracing",
"variadics",
]
[[package]]
name = "hydroflow_datalog"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41813c88b02f3bfa8f5962e125495aa47c8d382cf5d135b02da40af4342bc6fb"
dependencies = [
"hydroflow_datalog_core",
"proc-macro-crate 1.3.1",
"proc-macro2",
"quote",
"syn 2.0.43",
]
[[package]]
name = "hydroflow_datalog_core"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea77a3b2f09bba3d461f9ce0dee28798d3b07dafe77fc46de4675155f5925e53"
dependencies = [
"hydroflow_lang",
"proc-macro-crate 1.3.1",
"proc-macro2",
"quote",
"rust-sitter",
"rust-sitter-tool",
"slotmap",
"syn 2.0.43",
]
[[package]]
name = "hydroflow_lang"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3191eee8ef49b4a814e4c33a0ce0d7470b733dc6118ea744f7f15168c38803f"
dependencies = [
"auto_impl",
"clap 4.4.11",
"data-encoding",
"itertools 0.10.5",
"prettyplease 0.2.15",
"proc-macro2",
"quote",
"regex",
"serde",
"serde_json",
"slotmap",
"syn 2.0.43",
"webbrowser",
]
[[package]]
name = "hydroflow_macro"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9be25d2a927fe4e6afe3e204786e968e983f53f313cc561950ff1cd09ecd92fc"
dependencies = [
"hydroflow_lang",
"itertools 0.10.5",
"proc-macro-crate 1.3.1",
"proc-macro2",
"quote",
"syn 2.0.43",
]
[[package]]
name = "hyper"
version = "0.14.28"
@@ -4156,6 +4372,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
@@ -4259,6 +4478,28 @@ version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "jni"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
dependencies = [
"cesu8",
"cfg-if 1.0.0",
"combine",
"jni-sys",
"log",
"thiserror",
"walkdir",
"windows-sys 0.45.0",
]
[[package]]
name = "jni-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
version = "0.1.27"
@@ -4352,6 +4593,17 @@ dependencies = [
"regex",
]
[[package]]
name = "lattices"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f3bff82353a971b61106a49369cfc1bd8398661107eadcb5387fcd21c43cac9"
dependencies = [
"cc-traits",
"sealed",
"serde",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -4692,6 +4944,15 @@ dependencies = [
"libc",
]
[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
dependencies = [
"libc",
]
[[package]]
name = "maplit"
version = "1.0.2"
@@ -5253,6 +5514,12 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "ndk-context"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
[[package]]
name = "new_debug_unreachable"
version = "1.0.4"
@@ -5495,6 +5762,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "objc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
dependencies = [
"malloc_buf",
]
[[package]]
name = "object"
version = "0.32.2"
@@ -6844,6 +7120,16 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b7e158a385023d209d6d5f2585c4b468f6dcb3dd5aca9b75c4f1678c05bb375"
[[package]]
name = "pusherator"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd486cb5153e0d8fa91d3daebae48917ae299b2569cc79901922f3923dc312ef"
dependencies = [
"either",
"variadics",
]
[[package]]
name = "pyo3"
version = "0.19.2"
@@ -7126,6 +7412,12 @@ dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "raw-window-handle"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
[[package]]
name = "rawpointer"
version = "0.2.1"
@@ -7181,6 +7473,26 @@ dependencies = [
"thiserror",
]
[[package]]
name = "ref-cast"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53313ec9f12686aeeffb43462c3ac77aa25f590a5f630eb2cde0de59417b29c7"
dependencies = [
"ref-cast-impl",
]
[[package]]
name = "ref-cast-impl"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2566c4bf6845f2c2e83b27043c3f5dfcd5ba8f2937d6c00dc009bfb51a079dc4"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.43",
]
[[package]]
name = "regex"
version = "1.10.2"
@@ -7632,6 +7944,55 @@ dependencies = [
"ordered-multimap 0.7.1",
]
[[package]]
name = "rust-sitter"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a0f365b4eb9591dd3e685791389a932041b0dc6ccf5db1ec3d8913f67279365"
dependencies = [
"rust-sitter-macro",
"tree-sitter-c2rust",
]
[[package]]
name = "rust-sitter-common"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c0a0b1da7317031274502b7c52cbb7cf529e7d1e1f3e23876519372b173a94"
dependencies = [
"quote",
"syn 1.0.109",
]
[[package]]
name = "rust-sitter-macro"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d25e213e40efa00713547cc0f3529694aca547cfceb0839bbc9406632e14d410"
dependencies = [
"proc-macro2",
"quote",
"rust-sitter-common",
"syn 1.0.109",
]
[[package]]
name = "rust-sitter-tool"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "803c6596476a188a4dd18106eb927a926a202e00077cdaa5648dd620262af158"
dependencies = [
"cc",
"rust-sitter-common",
"serde",
"serde_json",
"syn 1.0.109",
"syn-inline-mod",
"tempfile",
"tree-sitter",
"tree-sitter-cli",
]
[[package]]
name = "rust_decimal"
version = "1.33.1"
@@ -8324,6 +8685,18 @@ version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]]
name = "sealed"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4a8caec23b7800fb97971a1c6ae365b6239aaeddfb934d6265f8505e795699d"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.43",
]
[[package]]
name = "secrecy"
version = "0.8.0"
@@ -8409,6 +8782,7 @@ version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"indexmap 2.1.0",
"itoa",
"ryu",
"serde",
@@ -8761,6 +9135,22 @@ dependencies = [
"autocfg",
]
[[package]]
name = "slotmap"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
dependencies = [
"serde",
"version_check",
]
[[package]]
name = "smallbitvec"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75ce4f9dc4a41b4c3476cc925f1efb11b66df373a8fde5d4b8915fa91b5d995e"
[[package]]
name = "smallvec"
version = "1.11.2"
@@ -9014,7 +9404,7 @@ dependencies = [
"crc",
"crossbeam-queue",
"digest",
"dirs",
"dirs 4.0.0",
"dotenvy",
"either",
"event-listener",
@@ -9381,6 +9771,16 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "syn-inline-mod"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b670f535364c67358ecffb60b9f2579f9b45d3c71e8cca6d45d22ee0fadaa7eb"
dependencies = [
"proc-macro2",
"syn 1.0.109",
]
[[package]]
name = "syn_derive"
version = "0.1.8"
@@ -9789,6 +10189,18 @@ dependencies = [
"crunchy",
]
[[package]]
name = "tiny_http"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82"
dependencies = [
"ascii",
"chunked_transfer",
"httpdate",
"log",
]
[[package]]
name = "tinytemplate"
version = "1.2.1"
@@ -9962,6 +10374,7 @@ dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
"tokio-util",
]
[[package]]
@@ -10332,6 +10745,117 @@ dependencies = [
"tracing-log 0.2.0",
]
[[package]]
name = "tree-sitter"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e747b1f9b7b931ed39a548c1fae149101497de3c1fc8d9e18c62c1a66c683d3d"
dependencies = [
"cc",
"regex",
]
[[package]]
name = "tree-sitter-c2rust"
version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee40a4d9cf5a30c199935f346887588239daceae4d1418d81b789276fffb8d91"
dependencies = [
"c2rust-bitfields",
"once_cell",
"regex",
]
[[package]]
name = "tree-sitter-cli"
version = "0.20.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae7e9d844d4d38e511a7b93fe8ced79f2a364c32fdea10d04546f1c8317d5a0c"
dependencies = [
"ansi_term",
"anyhow",
"atty",
"clap 2.34.0",
"difference",
"dirs 3.0.2",
"glob",
"html-escape",
"indexmap 1.9.3",
"lazy_static",
"log",
"regex",
"regex-syntax 0.6.29",
"rustc-hash",
"semver",
"serde",
"serde_json",
"smallbitvec",
"tiny_http",
"toml 0.5.11",
"tree-sitter",
"tree-sitter-config",
"tree-sitter-highlight",
"tree-sitter-loader",
"tree-sitter-tags",
"walkdir",
"webbrowser",
"which",
]
[[package]]
name = "tree-sitter-config"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5fec4cb27f052ead2246631b332dba0cb6af9a54ce012badee59c4b0ded5e03"
dependencies = [
"anyhow",
"dirs 3.0.2",
"serde",
"serde_json",
]
[[package]]
name = "tree-sitter-highlight"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "042342584c5a7a0b833d9fc4e2bdab3f9868ddc6c4b339a1e01451c6720868bc"
dependencies = [
"regex",
"thiserror",
"tree-sitter",
]
[[package]]
name = "tree-sitter-loader"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0b17eef4833c7c139abed66d562dfa23228e97e647597baf246fd56c21bbfaf"
dependencies = [
"anyhow",
"cc",
"dirs 3.0.2",
"libloading",
"once_cell",
"regex",
"serde",
"serde_json",
"tree-sitter",
"tree-sitter-highlight",
"tree-sitter-tags",
]
[[package]]
name = "tree-sitter-tags"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccb3f1376219530a37a809751ecf65aa35fd8b9c1c4ab6d4faf5f6a9eeda2c05"
dependencies = [
"memchr",
"regex",
"thiserror",
"tree-sitter",
]
[[package]]
name = "triomphe"
version = "0.1.11"
@@ -10693,6 +11217,12 @@ version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "utf8-width"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3"
[[package]]
name = "utf8parse"
version = "0.2.1"
@@ -10729,6 +11259,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "variadics"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4500f518837578bf2d62d9c12f47ecb5b5279da689574793b7bace8138b4784"
[[package]]
name = "vcpkg"
version = "0.2.15"
@@ -10903,6 +11439,23 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webbrowser"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b2391658b02c27719fc5a0a73d6e696285138e8b12fba9d4baa70451023c71"
dependencies = [
"core-foundation",
"home",
"jni",
"log",
"ndk-context",
"objc",
"raw-window-handle",
"url",
"web-sys",
]
[[package]]
name = "webpki"
version = "0.22.4"

View File

@@ -33,6 +33,7 @@ members = [
"src/datanode",
"src/datatypes",
"src/file-engine",
"src/flow",
"src/frontend",
"src/log-store",
"src/meta-client",

24
src/flow/Cargo.toml Normal file
View File

@@ -0,0 +1,24 @@
[package]
name = "flow"
version.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
api.workspace = true
bimap = "0.6.3"
common-error.workspace = true
common-macro.workspace = true
common-meta.workspace = true
common-query.workspace = true
common-telemetry.workspace = true
common-time.workspace = true
datatypes.workspace = true
hydroflow = "0.5.0"
itertools.workspace = true
serde.workspace = true
servers.workspace = true
session.workspace = true
snafu.workspace = true
tokio.workspace = true
tonic.workspace = true

18
src/flow/src/adapter.rs Normal file
View File

@@ -0,0 +1,18 @@
// 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.
//! for getting data from source and sending results to sink
//! and communicating with other parts of the database
pub(crate) mod error;

View File

@@ -0,0 +1,77 @@
// 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 std::any::Any;
use common_macro::stack_trace_debug;
use common_telemetry::common_error::ext::ErrorExt;
use common_telemetry::common_error::status_code::StatusCode;
use datatypes::data_type::ConcreteDataType;
use datatypes::value::Value;
use serde::{Deserialize, Serialize};
use servers::define_into_tonic_status;
use snafu::{Location, Snafu};
use crate::expr::EvalError;
#[derive(Snafu)]
#[snafu(visibility(pub))]
#[stack_trace_debug]
pub enum Error {
/// TODO(discord9): add detailed location of column
#[snafu(display("Failed to eval stream"))]
Eval {
source: EvalError,
location: Location,
},
#[snafu(display("Table not found: {name}"))]
TableNotFound { name: String, location: Location },
#[snafu(display("Table already exist: {name}"))]
TableAlreadyExist { name: String, location: Location },
#[snafu(display("Failed to join task"))]
JoinTask {
#[snafu(source)]
error: tokio::task::JoinError,
location: Location,
},
#[snafu(display("Invalid query: {reason}"))]
InvalidQuery { reason: String, location: Location },
#[snafu(display("No protobuf type for value: {value}"))]
NoProtoType { value: Value, location: Location },
}
pub type Result<T> = std::result::Result<T, Error>;
impl ErrorExt for Error {
fn status_code(&self) -> StatusCode {
match self {
Self::Eval { .. } | &Self::JoinTask { .. } => StatusCode::Internal,
&Self::TableAlreadyExist { .. } => StatusCode::TableAlreadyExists,
Self::TableNotFound { .. } => StatusCode::TableNotFound,
&Self::InvalidQuery { .. } => StatusCode::PlanQuery,
Self::NoProtoType { .. } => StatusCode::Unexpected,
}
}
fn as_any(&self) -> &dyn Any {
self
}
}
define_into_tonic_status!(Error);

22
src/flow/src/expr.rs Normal file
View File

@@ -0,0 +1,22 @@
// 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.
//! for declare Expression in dataflow, including map, reduce, id and join(TODO!) etc.
pub(crate) mod error;
mod id;
pub(crate) use id::{GlobalId, Id, LocalId};
pub(crate) use crate::expr::error::{EvalError, InvalidArgumentSnafu, OptimizeSnafu};

View File

@@ -0,0 +1,61 @@
// 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 std::any::Any;
use common_macro::stack_trace_debug;
use common_telemetry::common_error::ext::ErrorExt;
use common_telemetry::common_error::status_code::StatusCode;
use datatypes::data_type::ConcreteDataType;
use serde::{Deserialize, Serialize};
use snafu::{Location, Snafu};
/// EvalError is about errors happen on columnar evaluation
///
/// TODO(discord9): add detailed location of column/operator(instead of code) to errors tp help identify related column
#[derive(Snafu)]
#[snafu(visibility(pub))]
#[stack_trace_debug]
pub enum EvalError {
#[snafu(display("Division by zero"))]
DivisionByZero,
#[snafu(display("Type mismatch: expected {expected}, actual {actual}"))]
TypeMismatch {
expected: ConcreteDataType,
actual: ConcreteDataType,
location: Location,
},
/// can't nest datatypes error because EvalError need to be store in map and serialization
#[snafu(display("Fail to unpack from value to given type: {msg}"))]
TryFromValue { msg: String, location: Location },
#[snafu(display("Fail to cast value of type {from} to given type {to}"))]
CastValue {
from: ConcreteDataType,
to: ConcreteDataType,
source: datatypes::Error,
location: Location,
},
#[snafu(display("Invalid argument: {reason}"))]
InvalidArgument { reason: String, location: Location },
#[snafu(display("Internal error: {reason}"))]
Internal { reason: String, location: Location },
#[snafu(display("Optimize error: {reason}"))]
Optimize { reason: String, location: Location },
}

41
src/flow/src/expr/id.rs Normal file
View File

@@ -0,0 +1,41 @@
// 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 serde::{Deserialize, Serialize};
/// Global id's scope is in Current Worker, and is cross-dataflow
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum GlobalId {
/// System namespace.
System(u64),
/// User namespace.
User(u64),
/// Transient namespace.
Transient(u64),
/// Dummy id for query being explained
Explain,
}
/// Local id is used in local scope created by `Plan::Let{id: LocalId, value, body}`
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub struct LocalId(pub(crate) u64);
/// Id is used to identify a dataflow component in plan like `Plan::Get{id: Id}`
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum Id {
/// An identifier that refers to a local component of a dataflow.
Local(LocalId),
/// An identifier that refers to a global dataflow.
Global(GlobalId),
}

View File

@@ -0,0 +1,33 @@
// 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.
pub(crate) use func::AggregateFunc;
use serde::{Deserialize, Serialize};
use crate::expr::ScalarExpr;
mod func;
/// Describes an aggregation expression.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct AggregateExpr {
/// Names the aggregation function.
pub func: AggregateFunc,
/// An expression which extracts from each row the input to `func`.
/// TODO(discord9): currently unused, it only used in generate KeyValPlan from AggregateExpr
pub expr: ScalarExpr,
/// Should the aggregation be applied only to distinct results in each group.
#[serde(default)]
pub distinct: bool,
}

19
src/flow/src/lib.rs Normal file
View File

@@ -0,0 +1,19 @@
// 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.
#![allow(unused)]
// allow unused for now because it should be use later
mod adapter;
mod expr;
mod repr;

165
src/flow/src/repr.rs Normal file
View File

@@ -0,0 +1,165 @@
// 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.
//! basically a wrapper around the `datatype` crate
//! for basic Data Representation
mod relation;
use std::borrow::Borrow;
use std::slice::SliceIndex;
use api::helper::{pb_value_to_value_ref, value_to_grpc_value};
use api::v1::Row as ProtoRow;
use datatypes::data_type::ConcreteDataType;
use datatypes::types::cast;
use datatypes::types::cast::CastOption;
use datatypes::value::Value;
use itertools::Itertools;
pub(crate) use relation::{RelationDesc, RelationType};
use serde::{Deserialize, Serialize};
use snafu::ResultExt;
use crate::expr::error::{CastValueSnafu, EvalError};
/// System-wide Record count difference type.
pub type Diff = i64;
/// System-wide default timestamp type
pub type Timestamp = i64;
/// Default type for a repr of changes to a collection.
pub type DiffRow = (Row, Timestamp, Diff);
/// Convert a value that is or can be converted to Datetime to internal timestamp
pub fn value_to_internal_ts(value: Value) -> Result<Timestamp, EvalError> {
match value {
Value::DateTime(ts) => Ok(ts.val()),
arg => {
let arg_ty = arg.data_type();
let res = cast(arg, &ConcreteDataType::datetime_datatype()).context({
CastValueSnafu {
from: arg_ty,
to: ConcreteDataType::datetime_datatype(),
}
})?;
if let Value::DateTime(ts) = res {
Ok(ts.val())
} else {
unreachable!()
}
}
}
}
/// A row is a vector of values.
///
/// TODO(discord9): use a more efficient representation
/// i.e. more compact like raw u8 of \[tag0, value0, tag1, value1, ...\]
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize)]
pub struct Row {
pub inner: Vec<Value>,
}
impl Row {
pub fn empty() -> Self {
Self { inner: vec![] }
}
pub fn new(row: Vec<Value>) -> Self {
Self { inner: row }
}
pub fn get(&self, idx: usize) -> Option<&Value> {
self.inner.get(idx)
}
pub fn clear(&mut self) {
self.inner.clear();
}
/// clear and return the inner vector
///
/// useful if you want to reuse the vector as a buffer
pub fn packer(&mut self) -> &mut Vec<Value> {
self.inner.clear();
&mut self.inner
}
/// pack a iterator of values into a row
pub fn pack<I>(iter: I) -> Row
where
I: IntoIterator<Item = Value>,
{
Self {
inner: iter.into_iter().collect(),
}
}
/// unpack a row into a vector of values
pub fn unpack(self) -> Vec<Value> {
self.inner
}
pub fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = Value>,
{
self.inner.extend(iter);
}
pub fn into_iter(self) -> impl Iterator<Item = Value> {
self.inner.into_iter()
}
pub fn iter(&self) -> impl Iterator<Item = &Value> {
self.inner.iter()
}
pub fn len(&self) -> usize {
self.inner.len()
}
}
impl From<ProtoRow> for Row {
fn from(row: ProtoRow) -> Self {
Row::pack(
row.values
.iter()
.map(|pb_val| -> Value { pb_value_to_value_ref(pb_val, &None).into() }),
)
}
}
impl From<Row> for ProtoRow {
fn from(row: Row) -> Self {
let values = row
.unpack()
.into_iter()
.map(value_to_grpc_value)
.collect_vec();
ProtoRow { values }
}
}
#[test]
fn test_row() {
let row = Row::empty();
let row_1 = Row::new(vec![]);
assert_eq!(row, row_1);
let mut row_2 = Row::new(vec![Value::Int32(1), Value::Int32(2)]);
assert_eq!(row_2.get(0), Some(&Value::Int32(1)));
row_2.clear();
assert_eq!(row_2.get(0), None);
row_2
.packer()
.extend(vec![Value::Int32(1), Value::Int32(2)]);
assert_eq!(row_2.get(0), Some(&Value::Int32(1)));
row_2.extend(vec![Value::Int32(1), Value::Int32(2)]);
assert_eq!(row_2.len(), 4);
let row_3 = Row::pack(row_2.into_iter());
assert_eq!(row_3.len(), 4);
let row_4 = Row::pack(row_3.iter().cloned());
assert_eq!(row_3, row_4);
}

View File

@@ -0,0 +1,398 @@
// 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 datatypes::prelude::ConcreteDataType;
use serde::{Deserialize, Serialize};
use snafu::ensure;
use crate::adapter::error::{InvalidQuerySnafu, Result};
/// a set of column indices that are "keys" for the collection.
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
pub struct Key {
pub column_indices: Vec<usize>,
}
impl Key {
/// create a new Key
pub fn new() -> Self {
Self {
column_indices: Vec::new(),
}
}
/// create a new Key from a vector of column indices
pub fn from(mut column_indices: Vec<usize>) -> Self {
column_indices.sort_unstable();
Self { column_indices }
}
/// Add a column to Key
pub fn add_col(&mut self, col: usize) {
self.column_indices.push(col);
}
/// Add columns to Key
pub fn add_cols<I>(&mut self, cols: I)
where
I: IntoIterator<Item = usize>,
{
self.column_indices.extend(cols);
}
/// Remove a column from Key
pub fn remove_col(&mut self, col: usize) {
self.column_indices.retain(|&r| r != col);
}
/// get all columns in Key
pub fn get(&self) -> &Vec<usize> {
&self.column_indices
}
/// True if Key is empty
pub fn is_empty(&self) -> bool {
self.column_indices.is_empty()
}
/// True if all columns in self are also in other
pub fn subset_of(&self, other: &Key) -> bool {
self.column_indices
.iter()
.all(|c| other.column_indices.contains(c))
}
}
/// The type of a relation.
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
pub struct RelationType {
/// The type for each column, in order.
pub column_types: Vec<ColumnType>,
/// Sets of indices that are "keys" for the collection.
///
/// Each element in this list is a set of column indices, each with the
/// property that the collection contains at most one record with each
/// distinct set of values for each column. Alternately, for a specific set
/// of values assigned to the these columns there is at most one record.
///
/// A collection can contain multiple sets of keys, although it is common to
/// have either zero or one sets of key indices.
#[serde(default)]
pub keys: Vec<Key>,
/// optionally indicate the column that is TIME INDEX
pub time_index: Option<usize>,
}
impl RelationType {
/// Constructs a `RelationType` representing the relation with no columns and
/// no keys.
pub fn empty() -> Self {
RelationType::new(vec![])
}
/// Constructs a new `RelationType` from specified column types.
///
/// The `RelationType` will have no keys.
pub fn new(column_types: Vec<ColumnType>) -> Self {
RelationType {
column_types,
keys: Vec::new(),
time_index: None,
}
}
/// Adds a new key for the relation. Also sorts the key indices.
pub fn with_key(mut self, mut indices: Vec<usize>) -> Self {
indices.sort_unstable();
let key = Key::from(indices);
if !self.keys.contains(&key) {
self.keys.push(key);
}
self
}
pub fn with_keys(mut self, keys: Vec<Vec<usize>>) -> Self {
for key in keys {
self = self.with_key(key)
}
self
}
/// Computes the number of columns in the relation.
pub fn arity(&self) -> usize {
self.column_types.len()
}
/// Gets the index of the columns used when creating a default index.
pub fn default_key(&self) -> Vec<usize> {
if let Some(key) = self.keys.first() {
if key.is_empty() {
(0..self.column_types.len()).collect()
} else {
key.get().clone()
}
} else {
(0..self.column_types.len()).collect()
}
}
/// True if any collection described by `self` could safely be described by `other`.
///
/// In practice this means checking that the scalar types match exactly, and that the
/// nullability of `self` is at least as strict as `other`, and that all keys of `other`
/// contain some key of `self` (as a set of key columns is less strict than any subset).
pub fn subtypes(&self, other: &RelationType) -> bool {
if self.column_types.len() != other.column_types.len() {
return false;
}
for (col1, col2) in self.column_types.iter().zip(other.column_types.iter()) {
if col1.nullable && !col2.nullable {
return false;
}
if col1.scalar_type != col2.scalar_type {
return false;
}
}
let all_keys = other
.keys
.iter()
.all(|key1| self.keys.iter().any(|key2| key1.subset_of(key2)));
if !all_keys {
return false;
}
true
}
}
/// The type of a `Value`
///
/// [`ColumnType`] bundles information about the scalar type of a datum (e.g.,
/// Int32 or String) with its nullability.
///
/// To construct a column type, either initialize the struct directly, or
/// use the [`ScalarType::nullable`] method.
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
pub struct ColumnType {
/// The underlying scalar type (e.g., Int32 or String) of this column.
pub scalar_type: ConcreteDataType,
/// Whether this datum can be null.
#[serde(default = "return_true")]
pub nullable: bool,
}
/// This method exists solely for the purpose of making ColumnType nullable by
/// default in unit tests. The default value of a bool is false, and the only
/// way to make an object take on any other value by default is to pass it a
/// function that returns the desired default value. See
/// <https://github.com/serde-rs/serde/issues/1030>
#[inline(always)]
fn return_true() -> bool {
true
}
/// A description of the shape of a relation.
///
/// It bundles a [`RelationType`] with the name of each column in the relation.
/// Individual column names are optional.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
pub struct RelationDesc {
typ: RelationType,
names: Vec<ColumnName>,
}
impl RelationDesc {
/// Constructs a new `RelationDesc` that represents the empty relation
/// with no columns and no keys.
pub fn empty() -> Self {
RelationDesc {
typ: RelationType::empty(),
names: vec![],
}
}
/// Constructs a new `RelationDesc` from a `RelationType` and an iterator
/// over column names.
///
pub fn try_new<I, N>(typ: RelationType, names: I) -> Result<Self>
where
I: IntoIterator<Item = N>,
N: Into<ColumnName>,
{
let names: Vec<_> = names.into_iter().map(|name| name.into()).collect();
ensure!(
typ.arity() == names.len(),
InvalidQuerySnafu {
reason: format!(
"Length mismatch between RelationType {:?} and column names {:?}",
typ.column_types, names
)
}
);
Ok(RelationDesc { typ, names })
}
/// Constructs a new `RelationDesc` from a `RelationType` and an iterator
/// over column names.
///
/// # Panics
///
/// Panics if the arity of the `RelationType` is not equal to the number of
/// items in `names`.
pub fn new_unchecked<I, N>(typ: RelationType, names: I) -> Self
where
I: IntoIterator<Item = N>,
N: Into<ColumnName>,
{
let names: Vec<_> = names.into_iter().map(|name| name.into()).collect();
assert_eq!(typ.arity(), names.len());
RelationDesc { typ, names }
}
pub fn from_names_and_types<I, T, N>(iter: I) -> Self
where
I: IntoIterator<Item = (N, T)>,
T: Into<ColumnType>,
N: Into<ColumnName>,
{
let (names, types): (Vec<_>, Vec<_>) = iter.into_iter().unzip();
let types = types.into_iter().map(Into::into).collect();
let typ = RelationType::new(types);
Self::new_unchecked(typ, names)
}
/// Concatenates a `RelationDesc` onto the end of this `RelationDesc`.
pub fn concat(mut self, other: Self) -> Self {
let self_len = self.typ.column_types.len();
self.names.extend(other.names);
self.typ.column_types.extend(other.typ.column_types);
for k in other.typ.keys {
let k = k
.column_indices
.into_iter()
.map(|idx| idx + self_len)
.collect();
self = self.with_key(k);
}
self
}
/// Appends a column with the specified name and type.
pub fn with_column<N>(mut self, name: N, column_type: ColumnType) -> Self
where
N: Into<ColumnName>,
{
self.typ.column_types.push(column_type);
self.names.push(name.into());
self
}
/// Adds a new key for the relation.
pub fn with_key(mut self, indices: Vec<usize>) -> Self {
self.typ = self.typ.with_key(indices);
self
}
/// Drops all existing keys.
pub fn without_keys(mut self) -> Self {
self.typ.keys.clear();
self
}
/// Builds a new relation description with the column names replaced with
/// new names.
///
pub fn try_with_names<I, N>(self, names: I) -> Result<Self>
where
I: IntoIterator<Item = N>,
N: Into<ColumnName>,
{
Self::try_new(self.typ, names)
}
/// Computes the number of columns in the relation.
pub fn arity(&self) -> usize {
self.typ.arity()
}
/// Returns the relation type underlying this relation description.
pub fn typ(&self) -> &RelationType {
&self.typ
}
/// Returns an iterator over the columns in this relation.
pub fn iter(&self) -> impl Iterator<Item = (&ColumnName, &ColumnType)> {
self.iter_names().zip(self.iter_types())
}
/// Returns an iterator over the types of the columns in this relation.
pub fn iter_types(&self) -> impl Iterator<Item = &ColumnType> {
self.typ.column_types.iter()
}
/// Returns an iterator over the names of the columns in this relation.
pub fn iter_names(&self) -> impl Iterator<Item = &ColumnName> {
self.names.iter()
}
/// Finds a column by name.
///
/// Returns the index and type of the column named `name`. If no column with
/// the specified name exists, returns `None`. If multiple columns have the
/// specified name, the leftmost column is returned.
pub fn get_by_name(&self, name: &ColumnName) -> Option<(usize, &ColumnType)> {
self.iter_names()
.position(|n| n == name)
.map(|i| (i, &self.typ.column_types[i]))
}
/// Gets the name of the `i`th column.
///
/// # Panics
///
/// Panics if `i` is not a valid column index.
pub fn get_name(&self, i: usize) -> &ColumnName {
&self.names[i]
}
/// Mutably gets the name of the `i`th column.
///
/// # Panics
///
/// Panics if `i` is not a valid column index.
pub fn get_name_mut(&mut self, i: usize) -> &mut ColumnName {
&mut self.names[i]
}
/// Gets the name of the `i`th column if that column name is unambiguous.
///
/// If at least one other column has the same name as the `i`th column,
/// returns `None`. If the `i`th column has no name, returns `None`.
///
/// # Panics
///
/// Panics if `i` is not a valid column index.
pub fn get_unambiguous_name(&self, i: usize) -> Option<&ColumnName> {
let name = &self.names[i];
if self.iter_names().filter(|n| *n == name).count() == 1 {
Some(name)
} else {
None
}
}
}
/// The name of a column in a [`RelationDesc`].
pub type ColumnName = String;