diff --git a/Cargo.lock b/Cargo.lock index ba3bf32..e11c28a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,20 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "serde", + "version_check", + "zerocopy 0.7.35", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -26,6 +40,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -115,7 +135,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.72", ] [[package]] @@ -126,7 +146,16 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.72", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", ] [[package]] @@ -250,6 +279,27 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -261,6 +311,18 @@ name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] [[package]] name = "build-data" @@ -278,6 +340,12 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "bytecount" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" + [[package]] name = "byteorder" version = "1.5.0" @@ -349,10 +417,10 @@ version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.72", ] [[package]] @@ -395,6 +463,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "core-foundation" version = "0.9.4" @@ -411,6 +485,30 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "cpufeatures" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.4.2" @@ -429,12 +527,31 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "darling" version = "0.20.10" @@ -456,7 +573,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.72", ] [[package]] @@ -467,7 +584,18 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.72", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", ] [[package]] @@ -480,12 +608,30 @@ dependencies = [ "serde", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + [[package]] name = "dotenv" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "dyn-clone" version = "1.0.17" @@ -497,6 +643,9 @@ name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +dependencies = [ + "serde", +] [[package]] name = "encode_unicode" @@ -529,6 +678,33 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fancy-regex" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" +dependencies = [ + "bit-set", + "regex", +] + [[package]] name = "fastrand" version = "2.1.0" @@ -545,6 +721,17 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -575,6 +762,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fraction" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3027ae1df8d41b4bed2241c8fdad4acc1e7af60c8e17743534b545e77182d678" +dependencies = [ + "lazy_static", + "num", +] + [[package]] name = "futures-channel" version = "0.3.30" @@ -582,6 +779,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -601,6 +799,17 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + [[package]] name = "futures-io" version = "0.3.30" @@ -615,7 +824,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.72", ] [[package]] @@ -647,6 +856,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -654,8 +873,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -700,6 +921,28 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] [[package]] name = "heck" @@ -719,6 +962,33 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "http" version = "0.2.12" @@ -847,6 +1117,26 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "include_dir" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" +dependencies = [ + "glob", + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -893,6 +1183,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "iso8601" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924e5d73ea28f59011fec52a0d12185d496a9b075d360657aed2a5707f701153" +dependencies = [ + "nom", +] + [[package]] name = "itertools" version = "0.12.1" @@ -951,11 +1250,44 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonschema" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a071f4f7efc9a9118dfb627a0a94ef247986e1ab8606a4c806ae2b3aa3b6978" +dependencies = [ + "ahash", + "anyhow", + "base64 0.21.7", + "bytecount", + "clap", + "fancy-regex", + "fraction", + "getrandom", + "iso8601", + "itoa", + "memchr", + "num-cmp", + "once_cell", + "parking_lot", + "percent-encoding", + "regex", + "reqwest", + "serde", + "serde_json", + "time", + "url", + "uuid", +] + [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -973,6 +1305,23 @@ dependencies = [ "winapi", ] +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libsqlite3-sys" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -1016,6 +1365,16 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "memchr" version = "2.7.4" @@ -1038,6 +1397,12 @@ dependencies = [ "unicase", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.4" @@ -1093,6 +1458,8 @@ dependencies = [ "jni", "log", "mime", + "ndc-calcite-schema", + "ndc-calcite-values", "ndc-models", "ndc-sdk", "once_cell", @@ -1122,8 +1489,9 @@ dependencies = [ "anyhow", "build-data", "clap", + "include_dir", "insta", - "schemars", + "ndc-calcite-schema", "serde", "serde_json", "serde_yaml", @@ -1133,21 +1501,67 @@ dependencies = [ ] [[package]] -name = "ndc-models" +name = "ndc-calcite-schema" +version = "0.1.4" +dependencies = [ + "anyhow", + "clap", + "jni", + "jsonschema", + "ndc-calcite-values", + "ndc-models", + "ndc-sdk", + "once_cell", + "prometheus", + "schemars", + "serde", + "serde_json", + "smol_str", + "sqlx", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "ndc-calcite-values" version = "0.1.4" -source = "git+http://github.com/hasura/ndc-spec.git?tag=v0.1.4#20172e3b2552b78d16dbafcd047f559ced420309" +dependencies = [ + "anyhow", + "clap", + "jni", + "jsonschema", + "ndc-models", + "once_cell", + "prometheus", + "schemars", + "serde", + "serde_json", + "smol_str", + "sqlx", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "ndc-models" +version = "0.1.5" +source = "git+https://github.com/hasura/ndc-spec.git?tag=v0.1.5#78f52768bd02a8289194078a5abc2432c8e3a758" dependencies = [ "indexmap 2.2.6", + "ref-cast", "schemars", "serde", "serde_json", "serde_with", + "smol_str", ] [[package]] name = "ndc-sdk" -version = "0.1.4" -source = "git+https://github.com/hasura/ndc-sdk-rs.git?tag=v0.1.4#29adcb5983c1237e8a5f4732d5230c2ba8ab75d3" +version = "0.2.1" +source = "git+https://github.com/hasura/ndc-sdk-rs.git?tag=v0.2.2#9120a826037cc16f8261c83663fef63452f4d5c6" dependencies = [ "async-trait", "axum", @@ -1179,8 +1593,8 @@ dependencies = [ [[package]] name = "ndc-test" -version = "0.1.4" -source = "git+http://github.com/hasura/ndc-spec.git?tag=v0.1.4#20172e3b2552b78d16dbafcd047f559ced420309" +version = "0.1.5" +source = "git+https://github.com/hasura/ndc-spec.git?tag=v0.1.5#78f52768bd02a8289194078a5abc2432c8e3a758" dependencies = [ "async-trait", "clap", @@ -1192,11 +1606,22 @@ dependencies = [ "semver", "serde", "serde_json", + "smol_str", "thiserror", "tokio", "url", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1207,12 +1632,99 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-cmp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa" + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1220,6 +1732,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -1260,7 +1773,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.72", ] [[package]] @@ -1424,11 +1937,26 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.3", "smallvec", "windows-targets 0.52.6", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1452,20 +1980,41 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.72", ] [[package]] name = "pin-project-lite" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] [[package]] -name = "pin-utils" -version = "0.1.0" +name = "pkcs8" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] [[package]] name = "pkg-config" @@ -1485,7 +2034,7 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f" dependencies = [ - "zerocopy", + "zerocopy 0.6.6", ] [[package]] @@ -1532,7 +2081,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn", + "syn 2.0.72", ] [[package]] @@ -1580,6 +2129,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.5.3" @@ -1589,6 +2147,26 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "ref-cast" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "regex" version = "1.10.5" @@ -1692,6 +2270,26 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1897,7 +2495,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn", + "syn 2.0.72", ] [[package]] @@ -1962,7 +2560,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.72", ] [[package]] @@ -1973,7 +2571,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.72", ] [[package]] @@ -2038,7 +2636,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 2.0.72", ] [[package]] @@ -2054,6 +2652,28 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -2072,6 +2692,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + [[package]] name = "similar" version = "2.6.0" @@ -2117,6 +2747,237 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlformat" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f" +dependencies = [ + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" +dependencies = [ + "ahash", + "atoi", + "byteorder", + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashlink", + "hex", + "indexmap 2.2.6", + "log", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlformat", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "url", + "webpki-roots", +] + +[[package]] +name = "sqlx-macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 1.0.109", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" +dependencies = [ + "dotenvy", + "either", + "heck 0.4.1", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 1.0.109", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" +dependencies = [ + "atoi", + "base64 0.21.7", + "bitflags 2.6.0", + "byteorder", + "bytes", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" +dependencies = [ + "atoi", + "base64 0.21.7", + "bitflags 2.6.0", + "byteorder", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa" +dependencies = [ + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "sqlx-core", + "tracing", + "url", + "urlencoding", +] + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] [[package]] name = "strsim" @@ -2130,6 +2991,17 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.72" @@ -2197,7 +3069,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.72", ] [[package]] @@ -2292,7 +3164,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.72", ] [[package]] @@ -2444,7 +3316,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.72", ] [[package]] @@ -2538,9 +3410,15 @@ checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.72", ] +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicase" version = "2.7.0" @@ -2571,6 +3449,24 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -2606,6 +3502,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" + [[package]] name = "valuable" version = "0.1.0" @@ -2649,6 +3551,12 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" version = "0.2.92" @@ -2670,7 +3578,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.72", "wasm-bindgen-shared", ] @@ -2704,7 +3612,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.72", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2748,6 +3656,22 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "whoami" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +dependencies = [ + "redox_syscall 0.4.1", + "wasite", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3010,7 +3934,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.6.6", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive 0.7.35", ] [[package]] @@ -3021,7 +3954,18 @@ checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.72", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index aacd8cc..74ce7d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,15 +22,8 @@ must_use_candidate = { level = "allow" } wildcard_imports = { level = "allow" } [workspace.dependencies] -indexmap = "2.2.6" jni = { version = "0.21.1", features = ["invocation"] } -ndc-models = { git = "http://github.com/hasura/ndc-spec.git", tag = "v0.1.4" } -ndc-sdk = { git = "https://github.com/hasura/ndc-sdk-rs.git", tag = "v0.1.4" } -async-trait = "0.1.79" -axum = { version = "0.6.20", features = ["http2"] } axum-extra = "0.8.0" -bytes = "1.6.0" -clap = { version = "4.5.4", features = ["derive", "env"] } http = "0.2" mime = "0.3.17" opentelemetry = "0.22.0" @@ -39,22 +32,46 @@ opentelemetry-otlp = { version = "0.15.0", features = ["reqwest-client", "gzip-t opentelemetry-semantic-conventions = "0.14.0" opentelemetry_sdk = { version = "0.22.1", features = ["rt-tokio"] } opentelemetry-zipkin = "0.20.0" -prometheus = "0.13.3" -reqwest = "0.11.27" -serde = { version = "1.0.197", features = ["derive"] } -serde_json = { version = "1.0.115", features = ["raw_value"] } -schemars = "0.8.1" -thiserror = "1.0" -tokio = { version = "1.36.0", features = ["fs", "macros", "rt-multi-thread", "signal"] } tower-http = { version = "0.4.4", features = ["cors", "trace", "validate-request"] } -tracing = "0.1.40" tracing-opentelemetry = "0.23.0" tracing-subscriber = { version = "0.3", default-features = false, features = ["ansi", "env-filter", "fmt", "json"] } -url = "2.5.0" dotenv = "0.15.0" once_cell = "1.19.0" -anyhow = "1.0.86" -serde_yaml = "0.9" +ndc-models = { git = "https://github.com/hasura/ndc-spec.git", tag = "v0.1.5" } +ndc-sdk = { git = "https://github.com/hasura/ndc-sdk-rs.git", tag = "v0.2.2" } +ndc-test = { git = "https://github.com/hasura/ndc-spec.git", tag = "v0.1.5" } + +anyhow = "1" +async-trait = "0.1" +axum = "0.6" +axum-test-helper = "0.3" +build-data = "0.2" +bytes = "1" +clap = "4" +env_logger = "0.11" +hyper = "0.14" +indexmap = "2" insta = "1" +jsonschema = "0.17" +log = "0.4.22" +multimap = "0.9" +nonempty = "0.10" +percent-encoding = "2" +prometheus = "0.13" +ref-cast = "1" +reqwest = "0.11" +schemars = "0.8" +serde = "1" +serde_json = "1" +serde_yaml = "0.9" +similar-asserts = "1" +smol_str = "0.1" +sqlformat = "0.2" +sqlx = "0.7" tempfile = "3" -build-data = "0.2" \ No newline at end of file +test-each = "0.2" +thiserror = "1" +tokio = "1" +tracing = "0.1" +url = "2" +uuid = "1" \ No newline at end of file diff --git a/adapters/arrow/dev.local.configuration.json b/adapters/arrow/dev.local.configuration.json index 84ce872..cafbf76 100644 --- a/adapters/arrow/dev.local.configuration.json +++ b/adapters/arrow/dev.local.configuration.json @@ -1,5 +1,5 @@ { - "version": "4", + "version": "5", "$schema": "schema.json", "model_file_path": "./model.json", "fixes": true diff --git a/adapters/arrow/schema.json b/adapters/arrow/schema.json index 4ee9e59..1346c1e 100644 --- a/adapters/arrow/schema.json +++ b/adapters/arrow/schema.json @@ -126,7 +126,7 @@ "Version": { "type": "string", "enum": [ - "4" + "5" ] } } diff --git a/adapters/cassandra/dev.local.configuration.json b/adapters/cassandra/dev.local.configuration.json index 0303258..7ebf972 100644 --- a/adapters/cassandra/dev.local.configuration.json +++ b/adapters/cassandra/dev.local.configuration.json @@ -1,5 +1,5 @@ { - "version": "4", + "version": "5", "$schema": "schema.json", "model": { "version": "1.0", diff --git a/adapters/cassandra/root.crt b/adapters/cassandra/root.crt new file mode 100644 index 0000000..ea92a08 --- /dev/null +++ b/adapters/cassandra/root.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/adapters/cassandra/schema.json b/adapters/cassandra/schema.json index 4ee9e59..1346c1e 100644 --- a/adapters/cassandra/schema.json +++ b/adapters/cassandra/schema.json @@ -126,7 +126,7 @@ "Version": { "type": "string", "enum": [ - "4" + "5" ] } } diff --git a/adapters/csv/configuration.json b/adapters/csv/configuration.json index ce0536f..cef05dd 100644 --- a/adapters/csv/configuration.json +++ b/adapters/csv/configuration.json @@ -1,5 +1,5 @@ { - "version": "4", + "version": "5", "$schema": "schema.json", "model": { "version": "1.0", diff --git a/adapters/csv/dev.local.configuration.json b/adapters/csv/dev.local.configuration.json index 062e1a8..010bef2 100644 --- a/adapters/csv/dev.local.configuration.json +++ b/adapters/csv/dev.local.configuration.json @@ -1,5 +1,5 @@ { - "version": "4", + "version": "5", "$schema": "schema.json", "model": { "version": "1.0", diff --git a/adapters/csv/schema.json b/adapters/csv/schema.json index 95904f5..1346c1e 100644 --- a/adapters/csv/schema.json +++ b/adapters/csv/schema.json @@ -41,6 +41,9 @@ "jdbcUrl": { "type": "string" }, + "sqlDialectFactory": { + "type": "string" + }, "jdbcCatalog": { "type": "string" }, @@ -123,7 +126,7 @@ "Version": { "type": "string", "enum": [ - "4" + "5" ] } } diff --git a/adapters/db2/.env b/adapters/db2/.env new file mode 100644 index 0000000..18cf2aa --- /dev/null +++ b/adapters/db2/.env @@ -0,0 +1,6 @@ +JAR_DEPENDENCY_FOLDER=../../calcite-rs-jni/target/dependency +CALCITE_JAR=../../calcite-rs-jni/target/calcite-rs-jni-1.0-SNAPSHOT.jar +RUST_LOG=info +OTEL_LOGS_EXPORTER=console +OTEL_LOG_LEVEL=trace +LOG4J_CONFIGURATION_FILE=../../calcite-rs-jni/target/classes/log4j2.xml \ No newline at end of file diff --git a/config-templates/.env.local b/adapters/db2/.env.local similarity index 100% rename from config-templates/.env.local rename to adapters/db2/.env.local diff --git a/adapters/db2/dev.local.configuration.json b/adapters/db2/dev.local.configuration.json new file mode 100644 index 0000000..4c7d2d1 --- /dev/null +++ b/adapters/db2/dev.local.configuration.json @@ -0,0 +1,90 @@ +{ + "version": "5", + "$schema": "schema.json", + "model": { + "version": "1.0", + "defaultSchema": "db2", + "schemas": [ + { + "type": "jdbc", + "name": "db2", + "jdbcUser": "db2inst1", + "jdbcPassword": "mypassword", + "jdbcUrl": "jdbc:db2://192.168.86.233:50000/testdb" + } + ] + }, + "model_file_path": "./model.json", + "fixes": true, + "metadata": { + "PROJECTS": { + "schema": "db2", + "name": "PROJECTS", + "columns": { + "PROJECTID": { + "name": "PROJECTID", + "scalarType": "INTEGER", + "nullable": false + }, + "PROJECTNAME": { + "name": "PROJECTNAME", + "scalarType": "VARCHAR", + "nullable": true + }, + "DEPARTMENTID": { + "name": "DEPARTMENTID", + "scalarType": "INTEGER", + "nullable": true + } + }, + "primaryKeys": [], + "exportedKeys": [] + }, + "EMPLOYEES": { + "schema": "db2", + "name": "EMPLOYEES", + "columns": { + "FIRSTNAME": { + "name": "FIRSTNAME", + "scalarType": "VARCHAR", + "nullable": true + }, + "LASTNAME": { + "name": "LASTNAME", + "scalarType": "VARCHAR", + "nullable": true + }, + "EMPLOYEEID": { + "name": "EMPLOYEEID", + "scalarType": "INTEGER", + "nullable": false + }, + "DEPARTMENTID": { + "name": "DEPARTMENTID", + "scalarType": "INTEGER", + "nullable": true + } + }, + "primaryKeys": [], + "exportedKeys": [] + }, + "DEPARTMENTS": { + "schema": "db2", + "name": "DEPARTMENTS", + "columns": { + "DEPARTMENTNAME": { + "name": "DEPARTMENTNAME", + "scalarType": "VARCHAR", + "nullable": true + }, + "DEPARTMENTID": { + "name": "DEPARTMENTID", + "scalarType": "INTEGER", + "nullable": false + } + }, + "primaryKeys": [], + "exportedKeys": [] + } + } +} \ No newline at end of file diff --git a/config-templates/schema.json b/adapters/db2/schema.json similarity index 99% rename from config-templates/schema.json rename to adapters/db2/schema.json index 4ee9e59..1346c1e 100644 --- a/config-templates/schema.json +++ b/adapters/db2/schema.json @@ -126,7 +126,7 @@ "Version": { "type": "string", "enum": [ - "4" + "5" ] } } diff --git a/adapters/file/configuration.json b/adapters/file/configuration.json index 04d8e49..e54a3c9 100644 --- a/adapters/file/configuration.json +++ b/adapters/file/configuration.json @@ -1,5 +1,5 @@ { - "version": "4", + "version": "5", "$schema": "schema.json", "model": { "version": "1.0", diff --git a/adapters/file/connector-metadata.yaml b/adapters/file/connector-metadata.yaml deleted file mode 100644 index c153715..0000000 --- a/adapters/file/connector-metadata.yaml +++ /dev/null @@ -1,15 +0,0 @@ -packagingDefinition: - type: PrebuiltDockerImage - dockerImage: "kstott/file_connector:latest" -supportedEnvironmentVariables: - - name: DATA_FOLDER - description: A local directory of *.csv and *.json files -commands: - update: ndc-calcite update -cliPlugin: - name: elasticsearch - version: "v0.1.0" -dockerComposeWatch: - - path: ./ - target: /etc/ndc-calcite - action: sync+restart diff --git a/adapters/file/dev.local.configuration.json b/adapters/file/dev.local.configuration.json index c45fa45..7e4a845 100644 --- a/adapters/file/dev.local.configuration.json +++ b/adapters/file/dev.local.configuration.json @@ -1,5 +1,5 @@ { - "version": "4", + "version": "5", "$schema": "schema.json", "model": { "version": "1.0", @@ -10,7 +10,7 @@ "name": "sales", "factory": "org.apache.calcite.adapter.file.FileSchemaFactory", "operand": { - "directory": "/Users/kennethstott/Documents/GitHub/ndc-calcite/adapters/file/resources/test/sales" + "directory": "/Users/kennethstott/Documents/GitHub/ndc-calcite/adapters/file/resources/test/bug" } } ] @@ -18,48 +18,177 @@ "model_file_path": "./model.json", "fixes": true, "metadata": { - "EMPS": { + "ARCHERS": { "schema": "sales", - "name": "EMPS", + "name": "ARCHERS", "columns": { - "CITY": { - "name": "CITY", + "f": { + "name": "f", "scalarType": "VARCHAR", "nullable": true }, - "JOINEDAT": { - "name": "JOINEDAT", - "scalarType": "DATE", + "d": { + "name": "d", + "scalarType": "LIST", "nullable": true }, - "MANAGER": { - "name": "MANAGER", - "scalarType": "BOOLEAN", + "c": { + "name": "c", + "scalarType": "VARCHAR", + "nullable": true + }, + "e": { + "name": "e", + "scalarType": "VARCHAR", + "nullable": true + }, + "b": { + "name": "b", + "scalarType": "VARCHAR", + "nullable": true + }, + "object": { + "name": "object", + "scalarType": "VARCHAR", + "nullable": true + }, + "id": { + "name": "id", + "scalarType": "INTEGER", + "nullable": true + }, + "a": { + "name": "a", + "scalarType": "VARCHAR", + "nullable": true + }, + "g": { + "name": "g", + "scalarType": "LIST", + "nullable": true + } + }, + "primaryKeys": [], + "exportedKeys": [] + }, + "test": { + "schema": "sales", + "name": "test", + "columns": { + "id": { + "name": "id", + "scalarType": "INTEGER", + "nullable": true + }, + "d": { + "name": "d", + "scalarType": "LIST", + "nullable": true + }, + "b": { + "name": "b", + "scalarType": "VARCHAR", + "nullable": true + }, + "object": { + "name": "object", + "scalarType": "VARCHAR", "nullable": true }, + "f": { + "name": "f", + "scalarType": "VARCHAR", + "nullable": true + }, + "c": { + "name": "c", + "scalarType": "VARCHAR", + "nullable": true + }, + "g": { + "name": "g", + "scalarType": "LIST", + "nullable": true + }, + "a": { + "name": "a", + "scalarType": "VARCHAR", + "nullable": true + }, + "e": { + "name": "e", + "scalarType": "VARCHAR", + "nullable": true + } + }, + "primaryKeys": [], + "exportedKeys": [] + }, + "LONG_EMPS": { + "schema": "sales", + "name": "LONG_EMPS", + "columns": { "EMPID": { "name": "EMPID", "scalarType": "INTEGER", "nullable": true }, - "GENDER": { - "name": "GENDER", + "CITY": { + "name": "CITY", "scalarType": "VARCHAR", "nullable": true }, + "SLACKER": { + "name": "SLACKER", + "scalarType": "BOOLEAN", + "nullable": true + }, + "EMPNO": { + "name": "EMPNO", + "scalarType": "INTEGER", + "nullable": true + }, + "DEPTNO": { + "name": "DEPTNO", + "scalarType": "INTEGER", + "nullable": true + }, "AGE": { "name": "AGE", "scalarType": "INTEGER", "nullable": true }, + "GENDER": { + "name": "GENDER", + "scalarType": "VARCHAR", + "nullable": true + }, + "MANAGER": { + "name": "MANAGER", + "scalarType": "BOOLEAN", + "nullable": true + }, + "JOINEDAT": { + "name": "JOINEDAT", + "scalarType": "DATE", + "nullable": true + }, "NAME": { "name": "NAME", "scalarType": "VARCHAR", "nullable": true - }, - "DEPTNO": { - "name": "DEPTNO", - "scalarType": "INTEGER", + } + }, + "primaryKeys": [], + "exportedKeys": [] + }, + "WACKY_COLUMN_NAMES": { + "schema": "sales", + "name": "WACKY_COLUMN_NAMES", + "columns": { + "CITY": { + "name": "CITY", + "scalarType": "VARCHAR", "nullable": true }, "SLACKER": { @@ -71,41 +200,106 @@ "name": "EMPNO", "scalarType": "INTEGER", "nullable": true + }, + "2gender": { + "name": "2gender", + "scalarType": "VARCHAR", + "nullable": true + }, + "AGE": { + "name": "AGE", + "scalarType": "INTEGER", + "nullable": true + }, + "naME": { + "name": "naME", + "scalarType": "VARCHAR", + "nullable": true + }, + "EMPID": { + "name": "EMPID", + "scalarType": "INTEGER", + "nullable": true + }, + "MANAGER": { + "name": "MANAGER", + "scalarType": "BOOLEAN", + "nullable": true + }, + "DEPTNO": { + "name": "DEPTNO", + "scalarType": "VARCHAR", + "nullable": true + }, + "joined at": { + "name": "joined at", + "scalarType": "DATE", + "nullable": true } }, "primaryKeys": [], "exportedKeys": [] }, - "DEPTS": { + "configuration": { "schema": "sales", - "name": "DEPTS", + "name": "configuration", "columns": { - "NAME": { - "name": "NAME", + "metadata": { + "name": "metadata", + "scalarType": "MAP", + "nullable": true + }, + "fixes": { + "name": "fixes", "scalarType": "VARCHAR", "nullable": true }, - "DEPTNO": { - "name": "DEPTNO", - "scalarType": "INTEGER", + "$schema": { + "name": "$schema", + "scalarType": "VARCHAR", + "nullable": true + }, + "model": { + "name": "model", + "scalarType": "MAP", + "nullable": true + }, + "model_file_path": { + "name": "model_file_path", + "scalarType": "VARCHAR", + "nullable": true + }, + "version": { + "name": "version", + "scalarType": "VARCHAR", "nullable": true } }, "primaryKeys": [], "exportedKeys": [] }, - "SDEPTS": { + "DATES": { "schema": "sales", - "name": "SDEPTS", + "name": "DATES", "columns": { - "DEPTNO": { - "name": "DEPTNO", - "scalarType": "INTEGER", + "JOINTIMES": { + "name": "JOINTIMES", + "scalarType": "TIMESTAMP", "nullable": true }, - "NAME": { - "name": "NAME", - "scalarType": "VARCHAR", + "JOINEDAT": { + "name": "JOINEDAT", + "scalarType": "DATE", + "nullable": true + }, + "JOINTIME": { + "name": "JOINTIME", + "scalarType": "TIME", + "nullable": true + }, + "EMPNO": { + "name": "EMPNO", + "scalarType": "INTEGER", "nullable": true } }, diff --git a/adapters/file/resources/test/bug/configuration.json b/adapters/file/resources/test/bug/configuration.json index 03e8b00..f102c77 100644 --- a/adapters/file/resources/test/bug/configuration.json +++ b/adapters/file/resources/test/bug/configuration.json @@ -1,5 +1,5 @@ { - "version": "4", + "version": "5", "$schema": "schema.json", "model": { "version": "1.0", diff --git a/adapters/file/schema.json b/adapters/file/schema.json index 95904f5..1346c1e 100644 --- a/adapters/file/schema.json +++ b/adapters/file/schema.json @@ -41,6 +41,9 @@ "jdbcUrl": { "type": "string" }, + "sqlDialectFactory": { + "type": "string" + }, "jdbcCatalog": { "type": "string" }, @@ -123,7 +126,7 @@ "Version": { "type": "string", "enum": [ - "4" + "5" ] } } diff --git a/adapters/h2/dev.local.configuration.json b/adapters/h2/dev.local.configuration.json new file mode 100644 index 0000000..4c7d2d1 --- /dev/null +++ b/adapters/h2/dev.local.configuration.json @@ -0,0 +1,90 @@ +{ + "version": "5", + "$schema": "schema.json", + "model": { + "version": "1.0", + "defaultSchema": "db2", + "schemas": [ + { + "type": "jdbc", + "name": "db2", + "jdbcUser": "db2inst1", + "jdbcPassword": "mypassword", + "jdbcUrl": "jdbc:db2://192.168.86.233:50000/testdb" + } + ] + }, + "model_file_path": "./model.json", + "fixes": true, + "metadata": { + "PROJECTS": { + "schema": "db2", + "name": "PROJECTS", + "columns": { + "PROJECTID": { + "name": "PROJECTID", + "scalarType": "INTEGER", + "nullable": false + }, + "PROJECTNAME": { + "name": "PROJECTNAME", + "scalarType": "VARCHAR", + "nullable": true + }, + "DEPARTMENTID": { + "name": "DEPARTMENTID", + "scalarType": "INTEGER", + "nullable": true + } + }, + "primaryKeys": [], + "exportedKeys": [] + }, + "EMPLOYEES": { + "schema": "db2", + "name": "EMPLOYEES", + "columns": { + "FIRSTNAME": { + "name": "FIRSTNAME", + "scalarType": "VARCHAR", + "nullable": true + }, + "LASTNAME": { + "name": "LASTNAME", + "scalarType": "VARCHAR", + "nullable": true + }, + "EMPLOYEEID": { + "name": "EMPLOYEEID", + "scalarType": "INTEGER", + "nullable": false + }, + "DEPARTMENTID": { + "name": "DEPARTMENTID", + "scalarType": "INTEGER", + "nullable": true + } + }, + "primaryKeys": [], + "exportedKeys": [] + }, + "DEPARTMENTS": { + "schema": "db2", + "name": "DEPARTMENTS", + "columns": { + "DEPARTMENTNAME": { + "name": "DEPARTMENTNAME", + "scalarType": "VARCHAR", + "nullable": true + }, + "DEPARTMENTID": { + "name": "DEPARTMENTID", + "scalarType": "INTEGER", + "nullable": false + } + }, + "primaryKeys": [], + "exportedKeys": [] + } + } +} \ No newline at end of file diff --git a/adapters/h2/docker-compose.yaml b/adapters/h2/docker-compose.yaml new file mode 100644 index 0000000..0601dd9 --- /dev/null +++ b/adapters/h2/docker-compose.yaml @@ -0,0 +1,17 @@ +version: '3.8' + +services: + h2: + image: oscarfonts/h2:latest + container_name: h2-database + ports: + - "81:81" + - "1521:1521" + environment: + - H2_OPTIONS=-tcp -tcpAllowOthers -web -webAllowOthers -ifNotExists + volumes: + - h2-data:/opt/h2-data + - ./init.sql:/docker-entrypoint-initdb.d/init.sql + +volumes: + h2-data: diff --git a/adapters/h2/schema.json b/adapters/h2/schema.json new file mode 100644 index 0000000..1346c1e --- /dev/null +++ b/adapters/h2/schema.json @@ -0,0 +1,133 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ParsedConfiguration", + "type": "object", + "required": [ + "version" + ], + "properties": { + "version": { + "$ref": "#/definitions/Version" + }, + "$schema": { + "type": "string" + }, + "model": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "defaultSchema": { + "type": "string" + }, + "schemas": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "jdbcUser": { + "type": "string" + }, + "jdbcPassword": { + "type": "string" + }, + "jdbcUrl": { + "type": "string" + }, + "sqlDialectFactory": { + "type": "string" + }, + "jdbcCatalog": { + "type": "string" + }, + "jdbcSchema": {}, + "factory": { + "type": "string" + }, + "operand": { + "type": "object", + "properties": { + "directory": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "database": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "tables": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "factory": { + "type": "string" + }, + "operand": { + "type": "object", + "properties": { + "dataFormat": { + "type": "string" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "mapping": {} + }, + "required": [ + "name", + "type", + "mapping" + ] + } + }, + "keyDelimiter": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + } + }, + "definitions": { + "Version": { + "type": "string", + "enum": [ + "5" + ] + } + } +} \ No newline at end of file diff --git a/adapters/hive/dev.local.configuration.json b/adapters/hive/dev.local.configuration.json index 0c129c6..e25b8a8 100644 --- a/adapters/hive/dev.local.configuration.json +++ b/adapters/hive/dev.local.configuration.json @@ -1,5 +1,5 @@ { - "version": "4", + "version": "5", "$schema": "schema.json", "model": { "version": "1.0", diff --git a/adapters/hive/schema.json b/adapters/hive/schema.json new file mode 100644 index 0000000..1346c1e --- /dev/null +++ b/adapters/hive/schema.json @@ -0,0 +1,133 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ParsedConfiguration", + "type": "object", + "required": [ + "version" + ], + "properties": { + "version": { + "$ref": "#/definitions/Version" + }, + "$schema": { + "type": "string" + }, + "model": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "defaultSchema": { + "type": "string" + }, + "schemas": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "jdbcUser": { + "type": "string" + }, + "jdbcPassword": { + "type": "string" + }, + "jdbcUrl": { + "type": "string" + }, + "sqlDialectFactory": { + "type": "string" + }, + "jdbcCatalog": { + "type": "string" + }, + "jdbcSchema": {}, + "factory": { + "type": "string" + }, + "operand": { + "type": "object", + "properties": { + "directory": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "database": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "tables": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "factory": { + "type": "string" + }, + "operand": { + "type": "object", + "properties": { + "dataFormat": { + "type": "string" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "mapping": {} + }, + "required": [ + "name", + "type", + "mapping" + ] + } + }, + "keyDelimiter": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + } + }, + "definitions": { + "Version": { + "type": "string", + "enum": [ + "5" + ] + } + } +} \ No newline at end of file diff --git a/adapters/jdbc/configuration.json b/adapters/jdbc/configuration.json index a7a0451..459e4ac 100644 --- a/adapters/jdbc/configuration.json +++ b/adapters/jdbc/configuration.json @@ -1,5 +1,5 @@ { - "version": "4", + "version": "5", "$schema": "schema.json", "fixes": true, "model_file": "./model" diff --git a/adapters/jdbc/dev.local.configuration.json b/adapters/jdbc/dev.local.configuration.json index a3429c6..025eade 100644 --- a/adapters/jdbc/dev.local.configuration.json +++ b/adapters/jdbc/dev.local.configuration.json @@ -1,5 +1,5 @@ { - "version": "4", + "version": "5", "$schema": "schema.json", "model": { "version": "1.0", diff --git a/adapters/jdbc/schema.json b/adapters/jdbc/schema.json index 4ee9e59..1346c1e 100644 --- a/adapters/jdbc/schema.json +++ b/adapters/jdbc/schema.json @@ -126,7 +126,7 @@ "Version": { "type": "string", "enum": [ - "4" + "5" ] } } diff --git a/adapters/kafka/dev.local.configuration.json b/adapters/kafka/dev.local.configuration.json index b0ca42a..45c1cbb 100644 --- a/adapters/kafka/dev.local.configuration.json +++ b/adapters/kafka/dev.local.configuration.json @@ -1,5 +1,5 @@ { - "version": "4", + "version": "5", "$schema": "schema.json", "model_file_path": "./model.json", "fixes": true diff --git a/adapters/kafka/schema.json b/adapters/kafka/schema.json index 95904f5..1346c1e 100644 --- a/adapters/kafka/schema.json +++ b/adapters/kafka/schema.json @@ -41,6 +41,9 @@ "jdbcUrl": { "type": "string" }, + "sqlDialectFactory": { + "type": "string" + }, "jdbcCatalog": { "type": "string" }, @@ -123,7 +126,7 @@ "Version": { "type": "string", "enum": [ - "4" + "5" ] } } diff --git a/adapters/redis/schema.json b/adapters/redis/schema.json new file mode 100644 index 0000000..1346c1e --- /dev/null +++ b/adapters/redis/schema.json @@ -0,0 +1,133 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ParsedConfiguration", + "type": "object", + "required": [ + "version" + ], + "properties": { + "version": { + "$ref": "#/definitions/Version" + }, + "$schema": { + "type": "string" + }, + "model": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "defaultSchema": { + "type": "string" + }, + "schemas": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "jdbcUser": { + "type": "string" + }, + "jdbcPassword": { + "type": "string" + }, + "jdbcUrl": { + "type": "string" + }, + "sqlDialectFactory": { + "type": "string" + }, + "jdbcCatalog": { + "type": "string" + }, + "jdbcSchema": {}, + "factory": { + "type": "string" + }, + "operand": { + "type": "object", + "properties": { + "directory": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "database": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "tables": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "factory": { + "type": "string" + }, + "operand": { + "type": "object", + "properties": { + "dataFormat": { + "type": "string" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "mapping": {} + }, + "required": [ + "name", + "type", + "mapping" + ] + } + }, + "keyDelimiter": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + } + }, + "definitions": { + "Version": { + "type": "string", + "enum": [ + "5" + ] + } + } +} \ No newline at end of file diff --git a/adapters/sybase/.env b/adapters/sybase/.env new file mode 100644 index 0000000..18cf2aa --- /dev/null +++ b/adapters/sybase/.env @@ -0,0 +1,6 @@ +JAR_DEPENDENCY_FOLDER=../../calcite-rs-jni/target/dependency +CALCITE_JAR=../../calcite-rs-jni/target/calcite-rs-jni-1.0-SNAPSHOT.jar +RUST_LOG=info +OTEL_LOGS_EXPORTER=console +OTEL_LOG_LEVEL=trace +LOG4J_CONFIGURATION_FILE=../../calcite-rs-jni/target/classes/log4j2.xml \ No newline at end of file diff --git a/adapters/sybase/Dockerfile b/adapters/sybase/Dockerfile new file mode 100644 index 0000000..70d02a5 --- /dev/null +++ b/adapters/sybase/Dockerfile @@ -0,0 +1,4 @@ + +FROM datagrip/sybase:latest + +# Add any additional setup or dependencies here diff --git a/adapters/sybase/custom-entrypoint.sh b/adapters/sybase/custom-entrypoint.sh new file mode 100755 index 0000000..df2dd5a --- /dev/null +++ b/adapters/sybase/custom-entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Source Sybase environment setup script +source /opt/sybase/SYBASE.sh + +# Start Sybase +/opt/sybase/ASE-16_0/bin/dataserver -d/opt/sybase/data/master.dat & + +# Wait for Sybase to start +sleep 30 + +# Create the database +isql -U sa -P $SYBASE_PASSWORD -Q "CREATE DATABASE your_database;" + +# Keep the container running +tail -f /dev/null diff --git a/adapters/sybase/dev.local.configuration.json b/adapters/sybase/dev.local.configuration.json new file mode 100644 index 0000000..4c7d2d1 --- /dev/null +++ b/adapters/sybase/dev.local.configuration.json @@ -0,0 +1,90 @@ +{ + "version": "5", + "$schema": "schema.json", + "model": { + "version": "1.0", + "defaultSchema": "db2", + "schemas": [ + { + "type": "jdbc", + "name": "db2", + "jdbcUser": "db2inst1", + "jdbcPassword": "mypassword", + "jdbcUrl": "jdbc:db2://192.168.86.233:50000/testdb" + } + ] + }, + "model_file_path": "./model.json", + "fixes": true, + "metadata": { + "PROJECTS": { + "schema": "db2", + "name": "PROJECTS", + "columns": { + "PROJECTID": { + "name": "PROJECTID", + "scalarType": "INTEGER", + "nullable": false + }, + "PROJECTNAME": { + "name": "PROJECTNAME", + "scalarType": "VARCHAR", + "nullable": true + }, + "DEPARTMENTID": { + "name": "DEPARTMENTID", + "scalarType": "INTEGER", + "nullable": true + } + }, + "primaryKeys": [], + "exportedKeys": [] + }, + "EMPLOYEES": { + "schema": "db2", + "name": "EMPLOYEES", + "columns": { + "FIRSTNAME": { + "name": "FIRSTNAME", + "scalarType": "VARCHAR", + "nullable": true + }, + "LASTNAME": { + "name": "LASTNAME", + "scalarType": "VARCHAR", + "nullable": true + }, + "EMPLOYEEID": { + "name": "EMPLOYEEID", + "scalarType": "INTEGER", + "nullable": false + }, + "DEPARTMENTID": { + "name": "DEPARTMENTID", + "scalarType": "INTEGER", + "nullable": true + } + }, + "primaryKeys": [], + "exportedKeys": [] + }, + "DEPARTMENTS": { + "schema": "db2", + "name": "DEPARTMENTS", + "columns": { + "DEPARTMENTNAME": { + "name": "DEPARTMENTNAME", + "scalarType": "VARCHAR", + "nullable": true + }, + "DEPARTMENTID": { + "name": "DEPARTMENTID", + "scalarType": "INTEGER", + "nullable": false + } + }, + "primaryKeys": [], + "exportedKeys": [] + } + } +} \ No newline at end of file diff --git a/adapters/sybase/docker-compose.yaml b/adapters/sybase/docker-compose.yaml new file mode 100644 index 0000000..1c44911 --- /dev/null +++ b/adapters/sybase/docker-compose.yaml @@ -0,0 +1,20 @@ +version: '3.8' + +services: + sybase: + image: datagrip/sybase + container_name: sybase + ports: + - "5001:5000" + environment: + - SYBASE_USER=sa + - SYBASE_PASSWORD=your_password + - LD_LIBRARY_PATH=/opt/sybase/ASE-16_0/lib:/opt/sybase/OCS-16_0/lib:$LD_LIBRARY_PATH + volumes: + - sybase-data:/var/lib/sybase + - ./init-scripts:/docker-entrypoint-initdb.d + - ./custom-entrypoint.sh:/custom-entrypoint.sh + entrypoint: ["/bin/bash", "/custom-entrypoint.sh"] + +volumes: + sybase-data: diff --git a/adapters/sybase/init-scripts/create-database.sql b/adapters/sybase/init-scripts/create-database.sql new file mode 100644 index 0000000..3a2a689 --- /dev/null +++ b/adapters/sybase/init-scripts/create-database.sql @@ -0,0 +1,2 @@ +CREATE DATABASE test; +GO diff --git a/adapters/sybase/model.json b/adapters/sybase/model.json new file mode 100644 index 0000000..1329176 --- /dev/null +++ b/adapters/sybase/model.json @@ -0,0 +1,13 @@ +{ + "version": "1.0", + "defaultSchema": "db2", + "schemas": [ + { + "name": "db2", + "type": "jdbc", + "jdbcUrl": "jdbc:db2://192.168.86.233:50000/testdb", + "jdbcUser": "db2inst1", + "jdbcPassword": "mypassword" + } + ] +} diff --git a/adapters/sybase/schema.json b/adapters/sybase/schema.json new file mode 100644 index 0000000..1346c1e --- /dev/null +++ b/adapters/sybase/schema.json @@ -0,0 +1,133 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ParsedConfiguration", + "type": "object", + "required": [ + "version" + ], + "properties": { + "version": { + "$ref": "#/definitions/Version" + }, + "$schema": { + "type": "string" + }, + "model": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "defaultSchema": { + "type": "string" + }, + "schemas": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "jdbcUser": { + "type": "string" + }, + "jdbcPassword": { + "type": "string" + }, + "jdbcUrl": { + "type": "string" + }, + "sqlDialectFactory": { + "type": "string" + }, + "jdbcCatalog": { + "type": "string" + }, + "jdbcSchema": {}, + "factory": { + "type": "string" + }, + "operand": { + "type": "object", + "properties": { + "directory": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "database": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "tables": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "factory": { + "type": "string" + }, + "operand": { + "type": "object", + "properties": { + "dataFormat": { + "type": "string" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "mapping": {} + }, + "required": [ + "name", + "type", + "mapping" + ] + } + }, + "keyDelimiter": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + } + }, + "definitions": { + "Version": { + "type": "string", + "enum": [ + "5" + ] + } + } +} \ No newline at end of file diff --git a/adapters/trino/.env b/adapters/trino/.env new file mode 100644 index 0000000..18cf2aa --- /dev/null +++ b/adapters/trino/.env @@ -0,0 +1,6 @@ +JAR_DEPENDENCY_FOLDER=../../calcite-rs-jni/target/dependency +CALCITE_JAR=../../calcite-rs-jni/target/calcite-rs-jni-1.0-SNAPSHOT.jar +RUST_LOG=info +OTEL_LOGS_EXPORTER=console +OTEL_LOG_LEVEL=trace +LOG4J_CONFIGURATION_FILE=../../calcite-rs-jni/target/classes/log4j2.xml \ No newline at end of file diff --git a/adapters/trino/.env.local b/adapters/trino/.env.local new file mode 100644 index 0000000..afdaddc --- /dev/null +++ b/adapters/trino/.env.local @@ -0,0 +1,4 @@ +RUST_LOG=info +OTEL_LOGS_EXPORTER=console +OTEL_LOG_LEVEL=trace +OTEL_EXPORTER_OTLP_ENDPOINT=http://local.hasura.dev:4317 diff --git a/adapters/trino/config/config.properties b/adapters/trino/config/config.properties new file mode 100644 index 0000000..0dd1818 --- /dev/null +++ b/adapters/trino/config/config.properties @@ -0,0 +1,11 @@ +http-server.authentication.type=PASSWORD +internal-communication.shared-secret=TBf+WfgyRXCQN+8bevQTA5LOvfFRIsO4W/l6J06bUvo= +coordinator=true +node-scheduler.include-coordinator=true +http-server.http.port=8080 +discovery.uri=http://localhost:8080 +http-server.authentication.allow-insecure-over-http=true +http-server.https.enabled=true +http-server.https.port=8443 +http-server.https.keystore.path=/etc/trino/trino-keystore.jks +http-server.https.keystore.key=testtest diff --git a/adapters/trino/config/jvm.config b/adapters/trino/config/jvm.config new file mode 100644 index 0000000..b027857 --- /dev/null +++ b/adapters/trino/config/jvm.config @@ -0,0 +1,8 @@ +-Xmx16G +-XX:+UseG1GC +-XX:G1HeapRegionSize=32M +-XX:+UseGCOverheadLimit +-XX:+ExplicitGCInvokesConcurrent +-XX:+HeapDumpOnOutOfMemoryError +-XX:OnOutOfMemoryError=kill -9 %p +-XX:+ExitOnOutOfMemoryError diff --git a/adapters/trino/config/node.properties b/adapters/trino/config/node.properties new file mode 100644 index 0000000..0d1d3f4 --- /dev/null +++ b/adapters/trino/config/node.properties @@ -0,0 +1,3 @@ +node.environment=production +node.id=unique-node-id +node.data-dir=/var/trino/data diff --git a/adapters/trino/config/password-authenticator.properties b/adapters/trino/config/password-authenticator.properties new file mode 100644 index 0000000..9c76ffa --- /dev/null +++ b/adapters/trino/config/password-authenticator.properties @@ -0,0 +1,2 @@ +password-authenticator.name=file +file.password-file=/etc/trino/password.db diff --git a/adapters/trino/config/password.db b/adapters/trino/config/password.db new file mode 100644 index 0000000..ca0f12f --- /dev/null +++ b/adapters/trino/config/password.db @@ -0,0 +1 @@ +trino:trino diff --git a/adapters/trino/dev.local.configuration.json b/adapters/trino/dev.local.configuration.json new file mode 100644 index 0000000..4c7d2d1 --- /dev/null +++ b/adapters/trino/dev.local.configuration.json @@ -0,0 +1,90 @@ +{ + "version": "5", + "$schema": "schema.json", + "model": { + "version": "1.0", + "defaultSchema": "db2", + "schemas": [ + { + "type": "jdbc", + "name": "db2", + "jdbcUser": "db2inst1", + "jdbcPassword": "mypassword", + "jdbcUrl": "jdbc:db2://192.168.86.233:50000/testdb" + } + ] + }, + "model_file_path": "./model.json", + "fixes": true, + "metadata": { + "PROJECTS": { + "schema": "db2", + "name": "PROJECTS", + "columns": { + "PROJECTID": { + "name": "PROJECTID", + "scalarType": "INTEGER", + "nullable": false + }, + "PROJECTNAME": { + "name": "PROJECTNAME", + "scalarType": "VARCHAR", + "nullable": true + }, + "DEPARTMENTID": { + "name": "DEPARTMENTID", + "scalarType": "INTEGER", + "nullable": true + } + }, + "primaryKeys": [], + "exportedKeys": [] + }, + "EMPLOYEES": { + "schema": "db2", + "name": "EMPLOYEES", + "columns": { + "FIRSTNAME": { + "name": "FIRSTNAME", + "scalarType": "VARCHAR", + "nullable": true + }, + "LASTNAME": { + "name": "LASTNAME", + "scalarType": "VARCHAR", + "nullable": true + }, + "EMPLOYEEID": { + "name": "EMPLOYEEID", + "scalarType": "INTEGER", + "nullable": false + }, + "DEPARTMENTID": { + "name": "DEPARTMENTID", + "scalarType": "INTEGER", + "nullable": true + } + }, + "primaryKeys": [], + "exportedKeys": [] + }, + "DEPARTMENTS": { + "schema": "db2", + "name": "DEPARTMENTS", + "columns": { + "DEPARTMENTNAME": { + "name": "DEPARTMENTNAME", + "scalarType": "VARCHAR", + "nullable": true + }, + "DEPARTMENTID": { + "name": "DEPARTMENTID", + "scalarType": "INTEGER", + "nullable": false + } + }, + "primaryKeys": [], + "exportedKeys": [] + } + } +} \ No newline at end of file diff --git a/adapters/trino/docker-compose.yaml b/adapters/trino/docker-compose.yaml new file mode 100644 index 0000000..477443c --- /dev/null +++ b/adapters/trino/docker-compose.yaml @@ -0,0 +1,31 @@ +version: '3.8' + +services: + trino: + image: trinodb/trino:latest + ports: + - "8080:8080" + environment: + - JAVA_TOOL_OPTIONS=-Duser.timezone=UTC + depends_on: + - postgres + volumes: + - trino_catalog:/etc/trino/catalog + - ./config:/etc/trino + - ./var/trino:/var/trino + command: /usr/lib/trino/bin/run-trino + + postgres: + image: postgres:latest + environment: + POSTGRES_USER: trino + POSTGRES_PASSWORD: trino + POSTGRES_DB: postgres + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + +volumes: + trino_catalog: + postgres_data: diff --git a/adapters/trino/init.sql b/adapters/trino/init.sql new file mode 100644 index 0000000..e176ccb --- /dev/null +++ b/adapters/trino/init.sql @@ -0,0 +1,6 @@ +CREATE SCHEMA IF NOT EXISTS TEST; +CREATE TABLE IF NOT EXISTS TEST.USERS ( + ID INT PRIMARY KEY, + NAME VARCHAR(255) +); +INSERT INTO TEST.USERS (ID, NAME) VALUES (1, 'Alice'), (2, 'Bob'); diff --git a/adapters/trino/model.json b/adapters/trino/model.json new file mode 100644 index 0000000..5a56810 --- /dev/null +++ b/adapters/trino/model.json @@ -0,0 +1,15 @@ +{ + "version": "1.0", + "defaultSchema": "TEST", + "schemas": [ + { + "name": "TEST", + "type": "jdbc", + "jdbcDriver": "org.h2.Driver", + "jdbcUrl": "jdbc:h2:tcp://localhost:1521/test", + "jdbcSchema": "PUBLIC", + "jdbcUser": "sa", + "jdbcPassword": "" + } + ] +} diff --git a/adapters/trino/schema.json b/adapters/trino/schema.json new file mode 100644 index 0000000..1346c1e --- /dev/null +++ b/adapters/trino/schema.json @@ -0,0 +1,133 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ParsedConfiguration", + "type": "object", + "required": [ + "version" + ], + "properties": { + "version": { + "$ref": "#/definitions/Version" + }, + "$schema": { + "type": "string" + }, + "model": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "defaultSchema": { + "type": "string" + }, + "schemas": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "jdbcUser": { + "type": "string" + }, + "jdbcPassword": { + "type": "string" + }, + "jdbcUrl": { + "type": "string" + }, + "sqlDialectFactory": { + "type": "string" + }, + "jdbcCatalog": { + "type": "string" + }, + "jdbcSchema": {}, + "factory": { + "type": "string" + }, + "operand": { + "type": "object", + "properties": { + "directory": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "database": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "tables": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "factory": { + "type": "string" + }, + "operand": { + "type": "object", + "properties": { + "dataFormat": { + "type": "string" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "mapping": {} + }, + "required": [ + "name", + "type", + "mapping" + ] + } + }, + "keyDelimiter": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + } + }, + "definitions": { + "Version": { + "type": "string", + "enum": [ + "5" + ] + } + } +} \ No newline at end of file diff --git a/adapters/trino/trino_catalog/postgres.properties b/adapters/trino/trino_catalog/postgres.properties new file mode 100644 index 0000000..a0a088c --- /dev/null +++ b/adapters/trino/trino_catalog/postgres.properties @@ -0,0 +1,4 @@ +connector.name=postgresql +connection-url=jdbc:postgresql://postgres:5432/postgres +connection-user=trino +connection-password=trino diff --git a/build.sh b/build.sh index 6e55f48..2518908 100755 --- a/build.sh +++ b/build.sh @@ -4,8 +4,8 @@ release_info=$(curl -L \ -H "X-GitHub-Api-Version: 2022-11-28" \ https://api.github.com/repos/hasura/ndc-calcite/releases/latest) TAG=$(echo "$release_info" | grep 'tag_name' | awk -F':' '{print $2}' | tr -d ' "",') -tar -czvf connector-definition.tgz connector-definition docker build . --platform linux/arm64,linux/amd64 -t kstott/meta_connector:latest +docker buildx build --platform linux/arm64,linux/amd64 --output type=oci,dest=./image.tar . docker tag kstott/meta_connector:latest kstott/meta_connector:"$TAG" docker push kstott/meta_connector:latest docker push kstott/meta_connector:"$TAG" diff --git a/calcite-rs-jni/calcite b/calcite-rs-jni/calcite index 208afa6..1abb606 160000 --- a/calcite-rs-jni/calcite +++ b/calcite-rs-jni/calcite @@ -1 +1 @@ -Subproject commit 208afa6244338de9ebad251f6a9484d2385a0d06 +Subproject commit 1abb60601523203d56f947c735262fbe2045032e diff --git a/calcite-rs-jni/pom.xml b/calcite-rs-jni/pom.xml index 2345c9e..6d11eec 100644 --- a/calcite-rs-jni/pom.xml +++ b/calcite-rs-jni/pom.xml @@ -272,6 +272,11 @@ trino-jdbc 453 + + com.teradata.jdbc + terajdbc + 20.00.00.24 + com.microsoft.sqlserver mssql-jdbc diff --git a/calcite-rs-jni/src/main/java/org/kenstott/CalciteQuery.java b/calcite-rs-jni/src/main/java/org/kenstott/CalciteQuery.java index 574c6ab..68cd50f 100644 --- a/calcite-rs-jni/src/main/java/org/kenstott/CalciteQuery.java +++ b/calcite-rs-jni/src/main/java/org/kenstott/CalciteQuery.java @@ -241,7 +241,9 @@ private Map getTableColumnInfo(TableMetadata table) { entry("VARCHAR(65536) NOT NULL", "VARCHAR"), entry("VARCHAR NOT NULL", "VARCHAR"), entry("JavaType(class java.util.ArrayList)", "LIST"), + entry("JavaType(class org.apache.calcite.adapter.file.ComparableArrayList)", "LIST"), entry("JavaType(class java.util.LinkedHashMap)", "MAP"), + entry("JavaType(class org.apache.calcite.adapter.file.ComparableLinkedHashMap)", "MAP"), entry("JavaType(class java.lang.String)", "VARCHAR"), entry("JavaType(class java.lang.Integer)", "INTEGER"), entry("INTEGER NOT NULL", "INTEGER"), diff --git a/calcite-rs-jni/src/main/java/org/kenstott/Main.java b/calcite-rs-jni/src/main/java/org/kenstott/Main.java index 84ced05..8a23586 100644 --- a/calcite-rs-jni/src/main/java/org/kenstott/Main.java +++ b/calcite-rs-jni/src/main/java/org/kenstott/Main.java @@ -15,7 +15,7 @@ public class Main { public static void main(String[] args) { - String modelPath = "../adapters/h2/model.json"; + String modelPath = "../adapters/file/model.json"; String username = ""; String password = ""; Connection calciteConnection = null; @@ -34,7 +34,7 @@ public static void main(String[] args) { // """); // System.out.println(zz); String z1 = query.queryModels(""" - SELECT "ID" FROM "TEST"."PROJECTS" + SELECT COUNT("a") AS "a_count", COUNT(DISTINCT "a") AS "a_distinct_count", COUNT("b") AS "b_count", COUNT(DISTINCT "b") AS "b_distinct_count", COUNT("c") AS "c_count", COUNT(DISTINCT "c") AS "c_distinct_count", COUNT("d") AS "d_count", COUNT(DISTINCT "d") AS "d_distinct_count", COUNT("e") AS "e_count", COUNT(DISTINCT "e") AS "e_distinct_count", COUNT("f") AS "f_count", COUNT(DISTINCT "f") AS "f_distinct_count", COUNT("g") AS "g_count", COUNT(DISTINCT "g") AS "g_distinct_count", COUNT("id") AS "id_count", COUNT(DISTINCT "id") AS "id_distinct_count", COUNT("object") AS "object_count", COUNT(DISTINCT "object") AS "object_distinct_count" FROM "sales"."ARCHERS" LIMIT 10 """ ); System.out.println(z1); diff --git a/config-templates/configuration.json b/config-templates/configuration.json deleted file mode 100644 index f56d2ab..0000000 --- a/config-templates/configuration.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "version": "4", - "$schema": "schema.json", - "model_file_path": "/etc/connector/models/model.json", - "fixes": true -} diff --git a/config-templates/docker-compose.file.meta.yaml b/config-templates/docker-compose.file.meta.yaml deleted file mode 100644 index 6e830ae..0000000 --- a/config-templates/docker-compose.file.meta.yaml +++ /dev/null @@ -1,23 +0,0 @@ -services: - meta_connector: - build: - context: . - dockerfile_inline: |- - FROM docker.io/kstott/meta_connector:latest - develop: - watch: - - path: ./ - action: sync+restart - target: /etc/connector - env_file: - - .env.local - extra_hosts: - - local.hasura.dev=host-gateway - ports: - - mode: ingress - target: 8080 - published: ${HASURA_CONNECTOR_PORT:-8080} - protocol: tcp - volumes: - - .:/etc/connector - - ${MODEL_FILE:-./models/model.json}:/etc/connector/models/model.json diff --git a/connector-definition.tgz b/connector-definition.tgz deleted file mode 100644 index e66d18c..0000000 Binary files a/connector-definition.tgz and /dev/null differ diff --git a/connector-definition/connector-metadata.yaml b/connector-definition/connector-metadata.yaml deleted file mode 100644 index 4d394f0..0000000 --- a/connector-definition/connector-metadata.yaml +++ /dev/null @@ -1,15 +0,0 @@ -packagingDefinition: - type: PrebuiltDockerImage - dockerImage: "docker.io/kstott/meta_connector:latest" -supportedEnvironmentVariables: - - name: MODEL_FILE - description: The location of the calcite model file defaults to /etc/connector/models/model.json -commands: - update: hasura-calcite update -cliPlugin: - name: ndc-calcite-cli - version: "v0.1.0" -dockerComposeWatch: - - path: ./ - target: /etc/connector - action: sync+restart diff --git a/crates/calcite-schema/Cargo.toml b/crates/calcite-schema/Cargo.toml new file mode 100644 index 0000000..35a825c --- /dev/null +++ b/crates/calcite-schema/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "ndc-calcite-schema" +version.workspace = true +edition.workspace = true +license.workspace = true + +[lints] +workspace = true + +[dependencies] +ndc-calcite-values = { path = "../values"} +ndc-models = { workspace = true } +ndc-sdk = { workspace = true } +jni = { workspace = true } + +anyhow = { workspace = true } +# We only use clap for the derive feature. +clap = { workspace = true, features = ["derive", "env"] } +prometheus = {workspace = true } +schemars = { workspace = true, features = ["smol_str", "preserve_order"] } +serde = { workspace = true } +serde_json = { workspace = true, features = ["raw_value"] } +smol_str = { workspace = true } +sqlx = { workspace = true, features = ["json", "postgres", "runtime-tokio-rustls"] } +thiserror = { workspace = true } +tokio = { workspace = true, features = ["full"] } +tracing = { workspace = true } +once_cell = { workspace = true} + +[dev-dependencies] +jsonschema = { workspace = true } diff --git a/crates/connectors/ndc-calcite/src/aggregates.rs b/crates/calcite-schema/src/aggregates.rs similarity index 79% rename from crates/connectors/ndc-calcite/src/aggregates.rs rename to crates/calcite-schema/src/aggregates.rs index 758baea..84d13c5 100644 --- a/crates/connectors/ndc-calcite/src/aggregates.rs +++ b/crates/calcite-schema/src/aggregates.rs @@ -14,7 +14,7 @@ use std::collections::BTreeMap; -use ndc_models::{AggregateFunctionDefinition, Type}; +use ndc_models::{AggregateFunctionDefinition, AggregateFunctionName, Type}; /// Generates numeric aggregate functions for a given underlying type. /// @@ -28,13 +28,13 @@ use ndc_models::{AggregateFunctionDefinition, Type}; #[tracing::instrument] pub fn numeric_aggregates( underlying_type: &str, -) -> BTreeMap { - let aggregate_functions: BTreeMap = +) -> BTreeMap { + let aggregate_functions: BTreeMap = ["sum", "max", "avg", "min"] .iter() .map(|function| { ( - function.to_string(), + AggregateFunctionName::from(function.to_string()), aggregate_function_definition(underlying_type), ) }) diff --git a/crates/connectors/ndc-calcite/src/configuration.rs b/crates/calcite-schema/src/calcite.rs similarity index 82% rename from crates/connectors/ndc-calcite/src/configuration.rs rename to crates/calcite-schema/src/calcite.rs index b92d29a..2a1d514 100644 --- a/crates/connectors/ndc-calcite/src/configuration.rs +++ b/crates/calcite-schema/src/calcite.rs @@ -9,12 +9,14 @@ extern crate serde_json; use std::collections::HashMap; +use ndc_models::{FieldName}; +use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_json::Value; /// The type of the schema. // ANCHOR: Schema -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(PartialEq, Eq, JsonSchema, Serialize, Deserialize, Clone, Debug)] pub struct Schema { #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "type")] @@ -60,7 +62,7 @@ pub struct Schema { /// Represents a lattice in the schema. A lattice (in Calcite) /// refers to aggregates. -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Eq, PartialEq, JsonSchema, Serialize, Deserialize, Clone, Debug)] pub struct Lattice { #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, @@ -83,21 +85,21 @@ pub struct Lattice { pub tiles: Option>, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Eq, PartialEq, JsonSchema, Serialize, Deserialize, Clone, Debug)] pub struct Tile { #[serde(skip_serializing_if = "Option::is_none")] pub dimensions: Option, #[serde(skip_serializing_if = "Option::is_none")] pub measures: Option> } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Eq, PartialEq, JsonSchema, Serialize, Deserialize, Clone, Debug)] pub struct Measure { #[serde(skip_serializing_if = "Option::is_none")] pub agg: Option, #[serde(skip_serializing_if = "Option::is_none")] pub args: Option } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Eq, PartialEq, JsonSchema, Serialize, Deserialize, Clone, Debug)] pub struct Materialization { #[serde(skip_serializing_if = "Option::is_none")] pub view: Option, @@ -107,14 +109,14 @@ pub struct Materialization { pub sql: Option } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Eq, PartialEq, JsonSchema, Serialize, Deserialize, Clone, Debug)] pub struct Column { #[serde(rename = "type")] pub r#type: String, pub name: String, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Eq, PartialEq, JsonSchema, Serialize, Deserialize, Clone, Debug)] pub struct Type { #[serde(rename = "type")] pub r#type: String, @@ -130,7 +132,7 @@ pub struct Type { /// - `name` - The name of the table. It is an optional field. /// - `factory` - The factory of the table. It is an optional field. /// - `operand` - The operand of the table. It is an optional field. -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Eq, PartialEq, JsonSchema, Serialize, Deserialize, Clone, Debug)] pub struct Table { pub name: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -153,7 +155,7 @@ pub struct Table { } /// Represents a function. -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Eq, PartialEq, JsonSchema, Serialize, Deserialize, Clone, Debug)] pub struct Function { pub name: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -167,7 +169,7 @@ pub struct Function { } /// Represents the operand used in the schema. -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Eq, PartialEq, JsonSchema, Serialize, Deserialize, Clone, Debug)] pub struct Operand { #[serde(skip_serializing_if = "Option::is_none")] pub directory: Option, @@ -206,7 +208,7 @@ pub struct Operand { /// Represents a model. This is explained in greater detail /// in the Apache Calcite docs. -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(JsonSchema, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct Model { /// Calcite version pub version: String, @@ -223,38 +225,8 @@ pub struct Model { pub types: Option> } -/// Represents the configuration for the Calcite engine. -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct CalciteConfiguration { - /// Hasura NDC version - pub version: String, - /// JSON Schema file that defines a valid configuration - #[serde(rename = "$schema")] - pub _schema: String, - /// The Calcite Model - somewhat dependent on type of calcite adapter being used. - /// Better documentation can be found [here](https://calcite.apache.org/docs/model.html). - pub model: Option, - /// Used internally - pub model_file_path: Option, - #[serde(skip_serializing_if = "Option::is_none")] - /// Certain fixes that will solve for missing field values, for non-existing fields. - /// It's expensive and probably not necessary, but required to pass the NDC - /// tests. You can set the value to false in order to improve performance. - pub fixes: Option, - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(rename = "supportJsonObject")] - pub supports_json_object: Option, - #[serde(skip_serializing_if = "Option::is_none")] - /// Many common JDBC jars are included by default. Some are not you can - /// create a directory with additional required JARS and point to that - /// directory here. - pub jars: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub metadata: Option> -} - /// Represents an exported key between two tables in a database. -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(JsonSchema, Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] pub struct ExportedKey { #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "pkTableCatalog")] @@ -295,7 +267,7 @@ pub struct ExportedKey { /// - `columns` - A `HashMap` containing the columns of the table. /// - `primary_keys` - An optional `Vec` of primary key column names. /// - `exported_keys` - An optional `Vec` of exported keys to other tables. -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(JsonSchema, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct TableMetadata { #[serde(skip_serializing_if = "Option::is_none")] pub catalog: Option, @@ -304,7 +276,7 @@ pub struct TableMetadata { pub name: String, #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, - pub columns: HashMap, + pub columns: HashMap, #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "primaryKeys")] pub primary_keys: Option>, @@ -314,7 +286,7 @@ pub struct TableMetadata { } /// Represents the metadata of a column in a database table. -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(PartialEq, Eq, JsonSchema, Serialize, Deserialize, Clone, Debug)] pub struct ColumnMetadata { pub name: String, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/crates/connectors/ndc-calcite/src/collections.rs b/crates/calcite-schema/src/collections.rs similarity index 70% rename from crates/connectors/ndc-calcite/src/collections.rs rename to crates/calcite-schema/src/collections.rs index 9e2c18e..f8b8578 100644 --- a/crates/connectors/ndc-calcite/src/collections.rs +++ b/crates/calcite-schema/src/collections.rs @@ -5,10 +5,10 @@ use std::collections::{BTreeMap, HashMap}; use std::error::Error; -use ndc_models::{CollectionInfo, ForeignKeyConstraint, ObjectField, ObjectType, ScalarType, SchemaResponse, Type, UniquenessConstraint}; +use ndc_models::{CollectionInfo, CollectionName, FieldName, ForeignKeyConstraint, ObjectField, ObjectType, ObjectTypeName, ScalarType, ScalarTypeName, SchemaResponse, Type, TypeName, UniquenessConstraint}; use ndc_models::Type::{Named, Nullable}; -use crate::configuration::{ColumnMetadata, TableMetadata}; +use crate::calcite::{ColumnMetadata, TableMetadata}; /// Extracts information from data models and scalar types to generate object types and collection information. /// @@ -28,26 +28,27 @@ use crate::configuration::{ColumnMetadata, TableMetadata}; /// An inner Result can also be returned, which contains an error indicating an issue with the input data. // ANCHOR: collections pub fn collections( - data_models: &HashMap, - scalar_types: &BTreeMap, -) -> Result<(BTreeMap, Vec), Result>, > { - let mut object_types: BTreeMap = BTreeMap::new(); + data_models: &HashMap, + scalar_types: &BTreeMap, +) -> Result<(BTreeMap, Vec), Result>, > { + let mut object_types: BTreeMap = BTreeMap::new(); let mut collection_infos: Vec = Vec::new(); for (table, table_metadata) in data_models { let fields = build_fields(&table_metadata.columns); - if !scalar_types.contains_key(&table_metadata.name) { - object_types.insert(table.clone(), ObjectType { + let tbl_name = ScalarTypeName::new(table_metadata.name.clone().into()); + if !scalar_types.contains_key(&tbl_name) { + object_types.insert(ObjectTypeName::new(table.to_string().into()), ObjectType { description: table_metadata.description.clone(), fields, }, ); let uniqueness_constraints = build_uniqueness_constraints(&table_metadata); let foreign_keys = build_foreign_keys(&table_metadata, data_models); collection_infos.push(CollectionInfo { - name: table_metadata.name.clone(), + name: CollectionName::new(table_metadata.name.clone().parse().unwrap()), description: Some(format!("A collection of {}", table)), - collection_type: table_metadata.name.clone(), + collection_type: ObjectTypeName::new(TypeName::from(table_metadata.name.clone())), arguments: BTreeMap::new(), foreign_keys, uniqueness_constraints, @@ -66,16 +67,16 @@ pub fn collections( } #[tracing::instrument] -fn build_fields(column_metadata: &HashMap) -> BTreeMap { +fn build_fields(column_metadata: &HashMap) -> BTreeMap { column_metadata.iter().map(|(column_name, column_metadata)| { - let scalar_type = column_metadata.scalar_type.clone(); + let scalar_type = TypeName::from(column_metadata.scalar_type.clone()); let nullable = column_metadata.nullable.clone(); let final_type: Type = if nullable { Nullable { underlying_type: Box::new(Named { name: scalar_type }) } } else { Named { name: scalar_type } }; - (column_name.into(), + (column_name.clone(), ObjectField { description: column_metadata.description.clone(), r#type: final_type, @@ -87,12 +88,12 @@ fn build_fields(column_metadata: &HashMap) -> BTreeMap BTreeMap { let mut uc = BTreeMap::new(); uc.insert("PK".into(), UniquenessConstraint { - unique_columns: tb_metadata.primary_keys.clone().unwrap() + unique_columns: tb_metadata.primary_keys.clone().unwrap().iter().map(|s| FieldName::new(s.clone().parse().unwrap())).collect() }); uc } -fn build_foreign_keys(tb_metadata: &TableMetadata, data_models: &HashMap) -> BTreeMap { +fn build_foreign_keys(tb_metadata: &TableMetadata, data_models: &HashMap) -> BTreeMap { let mut constraints: BTreeMap = Default::default(); for (_, foreign_table_metadata) in data_models { for ft in foreign_table_metadata.clone().exported_keys.unwrap_or_default() { @@ -102,13 +103,13 @@ fn build_foreign_keys(tb_metadata: &TableMetadata, data_models: &HashMap { let mut constraint = ForeignKeyConstraint { column_mapping: Default::default(), - foreign_collection: pk_table_name.clone() + foreign_collection: CollectionName::from(pk_table_name.clone()) }; - constraint.column_mapping.insert(ft.fk_column_name, ft.pk_column_name); + constraint.column_mapping.insert(FieldName::from(ft.fk_column_name), FieldName::from(ft.pk_column_name)); constraints.insert(pk_table_name.clone(), constraint); } Some(value) => { - value.column_mapping.insert(ft.fk_column_name, ft.pk_column_name); + value.column_mapping.insert(FieldName::from(ft.fk_column_name), FieldName::from(ft.pk_column_name)); } } } diff --git a/crates/connectors/ndc-calcite/src/comparators.rs b/crates/calcite-schema/src/comparators.rs similarity index 82% rename from crates/connectors/ndc-calcite/src/comparators.rs rename to crates/calcite-schema/src/comparators.rs index 8b7041e..373188f 100644 --- a/crates/connectors/ndc-calcite/src/comparators.rs +++ b/crates/calcite-schema/src/comparators.rs @@ -4,7 +4,7 @@ //! use std::collections::BTreeMap; -use ndc_models::{ComparisonOperatorDefinition, Type}; +use ndc_models::{ComparisonOperatorDefinition, ComparisonOperatorName, Type, TypeName}; /// Generate string comparison operators based on the underlying type. /// @@ -25,8 +25,8 @@ use ndc_models::{ComparisonOperatorDefinition, Type}; // ANCHOR: string_comparators #[tracing::instrument] pub fn string_comparators( - numeric_comparison_operators: &BTreeMap, -) -> BTreeMap { + numeric_comparison_operators: &BTreeMap, +) -> BTreeMap { let mut string_comparison_operators = numeric_comparison_operators.clone(); string_comparison_operators.insert( "_like".into(), @@ -82,7 +82,7 @@ pub fn string_comparators( /// ``` // ANCHOR: numeric_comparators #[tracing::instrument] -pub fn numeric_comparators(underlying: String) -> BTreeMap { +pub fn numeric_comparators(underlying: String) -> BTreeMap { let numeric_comparison_operators = BTreeMap::from_iter([ ("_eq".into(), ComparisonOperatorDefinition::Equal), ("_in".into(), ComparisonOperatorDefinition::In), @@ -90,7 +90,7 @@ pub fn numeric_comparators(underlying: String) -> BTreeMap BTreeMap BTreeMap BTreeMap RootSchema { + SchemaSettings::openapi3() + .into_generator() + .into_root_schema_for::() +} pub const DEFAULT_CONNECTION_URI_VARIABLE: &str = "CONNECTION_URI"; @@ -19,7 +26,7 @@ pub const DEFAULT_CONNECTION_URI_VARIABLE: &str = "CONNECTION_URI"; /// Introducing a breaking configuration format change involves adding a new case to this type. /// /// 'ParsedConfiguration' is used to support serialization and deserialization of an NDC -/// configuration. It retains all the salient information that constitutes an instance of an NDC +/// configuration. It retains all the salient information that constitues an instance of an NDC /// deployment, such that 'c = parse_configuration(dir) => { write_parsed_configuration(c, dir2) ; /// assert(c == parse_configuration(dir2))}'. /// @@ -27,20 +34,18 @@ pub const DEFAULT_CONNECTION_URI_VARIABLE: &str = "CONNECTION_URI"; /// 'ParsedConfiguration' as well. #[derive(Clone, PartialEq, Eq, Debug)] pub enum ParsedConfiguration { - Version1 -} - -#[derive(Debug, Copy, Clone)] -pub enum VersionTag { - Version1 + Version5(version5::ParsedConfiguration), } +type Configuration = ParsedConfiguration; impl ParsedConfiguration { pub fn initial() -> Self { - ParsedConfiguration::Version1 + ParsedConfiguration::Version5(version5::ParsedConfiguration::empty()) } pub fn version(&self) -> VersionTag { - VersionTag::Version1 + match self { + ParsedConfiguration::Version5(_) => VersionTag::Version5, + } } } @@ -53,23 +58,30 @@ impl ParsedConfiguration { /// /// Separating 'ParsedConfiguration' and 'Configuration' simplifies the main query translation /// logic by placing the responsibility of dealing with configuration format evolution in -/// 'ParsedConfiguration. +/// 'ParsedConfiguration'. /// -#[derive(Debug)] -pub struct Configuration { - fake: String -} + + pub async fn introspect( input: ParsedConfiguration, + _context_path: &PathBuf, environment: impl Environment, ) -> anyhow::Result { - Ok(ParsedConfiguration::Version1) + match input { + ParsedConfiguration::Version5(config) => Ok(ParsedConfiguration::Version5( + version5::introspect(config, environment).await?, + )), + } } pub async fn parse_configuration( configuration_dir: impl AsRef + Send, ) -> Result { - Ok(ParsedConfiguration::Version1) + // Try parsing each supported version in turn + match version5::parse_configuration(configuration_dir.as_ref()).await { + Err(v5_err) => Err(v5_err), + Ok(config) => Ok(ParsedConfiguration::Version5(config)), + } } /// Turn a 'ParsedConfiguration' into a 'Configuration', such that it may be used in main @@ -79,9 +91,11 @@ pub async fn parse_configuration( /// into the runtime configuration. pub fn make_runtime_configuration( parsed_config: ParsedConfiguration, - environment: impl Environment, + _environment: impl Environment, ) -> Result { - Ok(Configuration{fake: "".to_string()}) + match parsed_config { + ParsedConfiguration::Version5(c) => Ok(ParsedConfiguration::Version5(c)), + } } /// Write out a parsed configuration to a directory. @@ -89,13 +103,17 @@ pub async fn write_parsed_configuration( parsed_config: ParsedConfiguration, out_dir: impl AsRef, ) -> Result<(), WriteParsedConfigurationError> { - Ok(()) + match parsed_config { + ParsedConfiguration::Version5(c) => version5::write_parsed_configuration(c, out_dir).await, + } } /// Produce an equivalent version of a parsed configuration in the latest supported version. /// /// This is part of the configuration crate API to enable users to upgrade their configurations -/// mechanically, using the ndc-calcite cli, when new versions are released.. +/// mechanically, using the ndc-postgres cli, when new versions are released. pub fn upgrade_to_latest_version(parsed_config: ParsedConfiguration) -> ParsedConfiguration { - ParsedConfiguration::Version1 + match parsed_config { + ParsedConfiguration::Version5(_) => parsed_config, + } } diff --git a/crates/cli/src/error.rs b/crates/calcite-schema/src/error.rs similarity index 100% rename from crates/cli/src/error.rs rename to crates/calcite-schema/src/error.rs diff --git a/crates/connectors/ndc-calcite/src/jvm.rs b/crates/calcite-schema/src/jvm.rs similarity index 95% rename from crates/connectors/ndc-calcite/src/jvm.rs rename to crates/calcite-schema/src/jvm.rs index 3d44c4d..0ce5558 100644 --- a/crates/connectors/ndc-calcite/src/jvm.rs +++ b/crates/calcite-schema/src/jvm.rs @@ -9,11 +9,10 @@ use std::sync::Mutex; use jni::{InitArgsBuilder, JavaVM, JNIVersion}; use once_cell::sync::OnceCell; use tracing::{event, Level}; - -use crate::configuration::CalciteConfiguration; +use crate::configuration::ParsedConfiguration; static JVM: OnceCell> = OnceCell::new(); -static CONFIG: OnceCell> = OnceCell::new(); +static CONFIG: OnceCell> = OnceCell::new(); /// Returns a reference to the global JVM instance. /// @@ -67,7 +66,11 @@ pub fn get_jvm() -> &'static Mutex { /// ``` // ANCHOR: init_jvm #[tracing::instrument] -pub fn init_jvm(calcite_configuration: &CalciteConfiguration) { +pub fn init_jvm(calcite_configuration: &ParsedConfiguration) { + let configuration = match calcite_configuration { + ParsedConfiguration::Version5(c) => c + }; + let state_inited = env::var("STATE_INITED").unwrap_or("false".to_string()); if state_inited == "false" { let folder_path = env::var("JAR_DEPENDENCY_FOLDER").unwrap_or("/calcite-rs-jni/target/dependency".into()); @@ -88,7 +91,7 @@ pub fn init_jvm(calcite_configuration: &CalciteConfiguration) { } } } - match &calcite_configuration.jars { + match &configuration.jars { Some(jars) => { if let Ok(entries) = fs::read_dir(jars) { for entry in entries { diff --git a/crates/calcite-schema/src/lib.rs b/crates/calcite-schema/src/lib.rs new file mode 100644 index 0000000..ab167ca --- /dev/null +++ b/crates/calcite-schema/src/lib.rs @@ -0,0 +1,18 @@ +pub mod schema; +pub mod scalars; +pub mod aggregates; +pub mod collections; + +pub mod comparators; +pub mod models; + +pub mod version5; +pub mod calcite; +pub mod environment; +pub mod error; +pub mod jvm; + +pub mod configuration; +pub mod version; +mod metrics; +mod test; \ No newline at end of file diff --git a/crates/calcite-schema/src/metrics.rs b/crates/calcite-schema/src/metrics.rs new file mode 100644 index 0000000..72353f7 --- /dev/null +++ b/crates/calcite-schema/src/metrics.rs @@ -0,0 +1,70 @@ +//! Metrics setup and update for our connector. + +use prometheus::{IntGauge, Registry}; + +use crate::version::VersionTag; + +/// The collection of configuration-related metrics exposed through the `/metrics` endpoint. +#[derive(Debug, Clone)] +pub struct Metrics { + configuration_version_3: IntGauge, + configuration_version_4: IntGauge, + configuration_version_5: IntGauge, +} + +impl Metrics { + /// Set up counters and gauges used to produce Prometheus metrics + pub fn initialize(metrics_registry: &mut Registry) -> Result { + let configuration_version_3 = add_int_gauge_metric( + metrics_registry, + "ndc_postgres_configuration_version_3", + "Get whether configuration version 3 is used", + )?; + + let configuration_version_4 = add_int_gauge_metric( + metrics_registry, + "ndc_postgres_configuration_version_4", + "Get whether configuration version 4 is used", + )?; + + let configuration_version_5 = add_int_gauge_metric( + metrics_registry, + "ndc_postgres_configuration_version_5", + "Get whether configuration version 5 is used", + )?; + + Ok(Self { + configuration_version_3, + configuration_version_4, + configuration_version_5, + }) + } + + /// Set the configuration version used by this connector instance. + pub fn set_configuration_version(&self, version: VersionTag) { + match version { + VersionTag::Version3 => self.configuration_version_3.set(1), + VersionTag::Version4 => self.configuration_version_4.set(1), + VersionTag::Version5 => self.configuration_version_5.set(1), + } + } +} + +/// Create a new int gauge metric and register it with the provided Prometheus Registry +fn add_int_gauge_metric( + metrics_registry: &mut Registry, + metric_name: &str, + metric_description: &str, +) -> Result { + let int_gauge = IntGauge::with_opts(prometheus::Opts::new(metric_name, metric_description))?; + register_collector(metrics_registry, int_gauge) +} + +/// Register a new collector with the registry, and returns it for later use. +fn register_collector( + metrics_registry: &mut Registry, + collector: Collector, +) -> Result { + metrics_registry.register(Box::new(collector.clone()))?; + Ok(collector) +} diff --git a/crates/calcite-schema/src/models.rs b/crates/calcite-schema/src/models.rs new file mode 100644 index 0000000..b5f7556 --- /dev/null +++ b/crates/calcite-schema/src/models.rs @@ -0,0 +1,39 @@ +use jni::objects::{GlobalRef, JObject, JString, JValueGen}; +use std::collections::HashMap; +use jni::objects::JValueGen::Object; +use ndc_models::CollectionName; +use crate::calcite::TableMetadata; +use tracing::{event, Level}; +use crate::jvm::get_jvm; + +/// Retrieves models from Calcite. +/// +/// # Arguments +/// +/// * `calcite_ref` - A reference to the Calcite instance. +/// +/// # Return +/// +/// A `HashMap` containing the retrieved models. The outer `HashMap` maps model names +/// to inner `HashMap`s, where each inner `HashMap` represents a model with its properties. +#[tracing::instrument] +pub fn get_models(calcite_ref: GlobalRef) -> HashMap { + let jvm = get_jvm().lock().unwrap(); + let env = jvm.attach_current_thread().unwrap(); + let calcite_query = env.new_local_ref(calcite_ref).unwrap(); + let mut env = jvm.attach_current_thread_as_daemon().unwrap(); + let args: &[JValueGen<&JObject<'_>>] = &[]; + let method_signature = "()Ljava/lang/String;"; + let result = env.call_method(calcite_query, "getModels", method_signature, args); + let map= match result.unwrap() { + Object(obj) => { + let j_string = JString::from(obj); + let json_string: String = env.get_string(&j_string).unwrap().into(); + let map: HashMap = serde_json::from_str(&json_string).unwrap(); + map + } + _ => todo!(), + }; + event!(Level::INFO, "Retrieved models from Calcite"); + return map; +} diff --git a/crates/connectors/ndc-calcite/src/scalars.rs b/crates/calcite-schema/src/scalars.rs similarity index 97% rename from crates/connectors/ndc-calcite/src/scalars.rs rename to crates/calcite-schema/src/scalars.rs index 65aaabf..e7e94df 100644 --- a/crates/connectors/ndc-calcite/src/scalars.rs +++ b/crates/calcite-schema/src/scalars.rs @@ -4,7 +4,7 @@ //! use std::collections::BTreeMap; -use ndc_models::{ComparisonOperatorDefinition, ScalarType, TypeRepresentation}; +use ndc_models::{ComparisonOperatorDefinition, ScalarType, ScalarTypeName, TypeRepresentation}; use crate::{aggregates, comparators}; @@ -68,7 +68,7 @@ use crate::{aggregates, comparators}; /// ``` // ANCHOR: scalars #[tracing::instrument] -pub fn scalars() -> BTreeMap { +pub fn scalars() -> BTreeMap { let string_comparison_operators = comparators::string_comparators(&comparators::numeric_comparators("VARCHAR".into())); let scalar_types = BTreeMap::from_iter([ diff --git a/crates/connectors/ndc-calcite/src/schema.rs b/crates/calcite-schema/src/schema.rs similarity index 86% rename from crates/connectors/ndc-calcite/src/schema.rs rename to crates/calcite-schema/src/schema.rs index 5a465af..21bc69e 100644 --- a/crates/connectors/ndc-calcite/src/schema.rs +++ b/crates/calcite-schema/src/schema.rs @@ -3,6 +3,7 @@ //! Introspects Calcite metadata and generates a new schema. Updates //! the config file with the new schema. //! + use std::error::Error; use std::fs::File; use std::io::Write; @@ -10,12 +11,14 @@ use std::path::Path; use jni::objects::GlobalRef; use ndc_models as models; -use ndc_models::SchemaResponse; +use ndc_models::{SchemaResponse}; use tracing::{event, Level}; -use crate::{calcite, collections, scalars}; -use crate::configuration::CalciteConfiguration; -use crate::connector::calcite::{CONFIG_FILE_NAME, DEV_CONFIG_FILE_NAME, is_running_in_container}; +use ndc_calcite_values::is_running_in_container::is_running_in_container; +use ndc_calcite_values::values::{CONFIG_FILE_NAME, DEV_CONFIG_FILE_NAME}; +use crate::version5::ParsedConfiguration; +use crate::models::get_models; +use crate::{collections, scalars}; /// Get the schema information from the given `calcite_ref`. /// @@ -61,15 +64,14 @@ use crate::connector::calcite::{CONFIG_FILE_NAME, DEV_CONFIG_FILE_NAME, is_runni /// ``` // ANCHOR: get_schema #[tracing::instrument] -pub fn get_schema(configuration: &CalciteConfiguration, calcite_ref: GlobalRef) -> Result> { +pub fn get_schema(configuration: &ParsedConfiguration, calcite_ref: GlobalRef) -> Result> { event!(Level::INFO, "in get_schema"); - let data_models = calcite::get_models(calcite_ref); + let data_models = get_models(calcite_ref); let scalar_types = scalars::scalars(); let (object_types, collections) = match collections::collections(&data_models, &scalar_types) { Ok(value) => value, Err(value) => return value, - }; - let procedures = vec![]; + }; let procedures = vec![]; let functions: Vec = vec![]; let schema = SchemaResponse { scalar_types, diff --git a/crates/calcite-schema/src/test.rs b/crates/calcite-schema/src/test.rs new file mode 100644 index 0000000..6d05cbb --- /dev/null +++ b/crates/calcite-schema/src/test.rs @@ -0,0 +1,48 @@ +#[cfg(test)] +pub mod common { + use std::fmt::Write; + use std::path::{Path, PathBuf}; + + /// Find the project root via the crate root provided by `cargo test`, + /// and get our single static configuration file. + /// This depends on the convention that all our crates live in `/crates/` + /// and will break in the unlikely case that we change this + pub fn get_path_from_project_root(ndc_metadata_path: impl AsRef) -> PathBuf { + let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + d.push("../../"); + d.push(ndc_metadata_path); + + d + } + /// Checks that a given value conforms to the schema generated by `schemars`. + /// + /// Panics with a human-readable error if the value does not conform, or if the + /// schema could not be compiled. + pub fn check_value_conforms_to_schema(value: &serde_json::Value) { + let schema_json = serde_json::to_value(schemars::schema_for!(T)) + .expect("the schema could not be converted to JSON"); + let schema = jsonschema::JSONSchema::options() + .with_draft(jsonschema::Draft::Draft7) + .compile(&schema_json) + .expect("the schema could not be compiled"); + + let result = schema.validate(value); + + match result { + Ok(()) => (), + Err(errors) => { + panic!( + "The configuration does not conform to the schema.\n{}", + errors.fold(String::new(), |mut str, error| { + let _ = write!( + str, + "{}\ninstance path: {}\nschema path: {}\n\n", + error, error.instance_path, error.schema_path + ); + str + }) + ) + } + } + } +} diff --git a/crates/calcite-schema/src/version.rs b/crates/calcite-schema/src/version.rs new file mode 100644 index 0000000..e908a80 --- /dev/null +++ b/crates/calcite-schema/src/version.rs @@ -0,0 +1,23 @@ +#[derive(Debug, Copy, Clone)] +pub enum VersionTag { + Version3, + Version4, + Version5, +} + +/// Emit deprecation warning text if the version is deprecated. +pub fn deprecated_config_warning(version: VersionTag) -> Option { + match version { + VersionTag::Version3 => Some( + "Warning: ndc-postgres configuration version '3' is deprecated. +Consider upgrading to the latest version: +https://hasura.io/docs/3.0/connectors/postgresql/configuration-reference/#upgrading-the-configuration-format-version".to_string() + ), + VersionTag::Version4 => Some( + "Warning: ndc-postgres configuration version '4' is deprecated. +Consider upgrading to the latest version: +https://hasura.io/docs/3.0/connectors/postgresql/configuration-reference/#upgrading-the-configuration-format-version".to_string() + ), + VersionTag::Version5 => None, + } +} diff --git a/crates/calcite-schema/src/version5.rs b/crates/calcite-schema/src/version5.rs new file mode 100644 index 0000000..e7ba1aa --- /dev/null +++ b/crates/calcite-schema/src/version5.rs @@ -0,0 +1,198 @@ +//! Internal Configuration and state for our connector. + +use std::collections::{HashMap}; +use std::path::Path; +use jni::JNIEnv; +use jni::objects::{GlobalRef, JObject, JValueGen, JValueOwned}; +use jni::objects::JValueGen::Object; +use ndc_models::CollectionName; +use ndc_sdk::connector::InitializationError; + +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use tokio::fs; +use tracing::{event, Level}; +use ndc_calcite_values::values::{CONFIGURATION_FILENAME, CONFIGURATION_JSONSCHEMA_FILENAME}; + +use crate::calcite::{Model, TableMetadata}; +use crate::environment::Environment; +use crate::error::{ParseConfigurationError, WriteParsedConfigurationError}; +use crate::jvm::{get_jvm, init_jvm}; +use crate::models::get_models; + +/// Initial configuration, just enough to connect to a database and elaborate a full +/// 'Configuration'. +#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize, JsonSchema)] +pub struct ParsedConfiguration { + /// Hasura NDC version + pub version: Version, + /// JSON Schema file that defines a valid configuration + #[serde(rename = "$schema")] + pub _schema: Option, + /// The Calcite Model - somewhat dependent on type of calcite adapter being used. + /// Better documentation can be found [here](https://calcite.apache.org/docs/model.html). + #[serde(skip_serializing_if = "Option::is_none")] + pub model: Option, + /// Used internally + pub model_file_path: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// Certain fixes that will solve for missing field values, for non-existing fields. + /// It's expensive and probably not necessary, but required to pass the NDC + /// tests. You can set the value to false in order to improve performance. + pub fixes: Option, + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(rename = "supportJsonObject")] + pub supports_json_object: Option, + #[serde(skip_serializing_if = "Option::is_none")] + /// Many common JDBC jars are included by default. Some are not you can + /// create a directory with additional required JARS and point to that + /// directory here. + pub jars: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option> +} + +#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize, JsonSchema)] +pub enum Version { + #[serde(rename = "5")] + This, +} + +impl ParsedConfiguration { + pub fn empty() -> Self { + Self { + version: Version::This, + _schema: Some(CONFIGURATION_JSONSCHEMA_FILENAME.to_string()), + model: None, + model_file_path: Some("./models/model.json".to_string()), + fixes: Some(true), + supports_json_object: None, + jars: None, + metadata: None, + } + } +} + +pub fn create_calcite_connection<'a>( + configuration: &ParsedConfiguration, + calcite_query: &JObject<'a>, + env: &'a mut JNIEnv<'a>, +) -> Result, InitializationError> { + let calcite_model = configuration.clone().model_file_path.unwrap_or_default(); + let arg0: JObject = env.new_string(calcite_model).unwrap().into(); + let args: &[JValueGen<&JObject<'_>>] = &[Object(&arg0)]; + let method_signature = "(Ljava/lang/String;)Ljava/sql/Connection;"; + let result = env.call_method( + calcite_query, + "createCalciteConnection", + method_signature, + args, + ); + + match result { + Ok(val) => { + event!(Level::INFO, "Connected to Calcite"); + Ok(val) + } + Err(e) => { + event!(Level::ERROR, "Error while connecting to Calcite: {:?}", e); + Err(InitializationError::Other(Box::new(e))) + } + } +} +pub fn create_calcite_query_engine<'a>(configuration: &ParsedConfiguration, env: &'a mut JNIEnv<'a>) -> JObject<'a> { + let class = env.find_class("org/kenstott/CalciteQuery").unwrap(); + let instance = env.new_object(class, "()V", &[]).unwrap(); + let _ = create_calcite_connection(configuration, &instance, env); + event!(Level::INFO, "Instantiated Calcite Query Engine"); + return instance; +} +pub async fn introspect( + args: ParsedConfiguration, + _environment: impl Environment, +) -> anyhow::Result { + init_jvm(&crate::configuration::ParsedConfiguration::Version5(args.clone())); + let calcite_ref: GlobalRef; + { + let java_vm = get_jvm().lock().unwrap(); + let mut env = java_vm.attach_current_thread_as_daemon().unwrap(); + let calcite = create_calcite_query_engine(&args, &mut env); + let env = java_vm.attach_current_thread_as_daemon().unwrap(); + calcite_ref = env.new_global_ref(calcite).unwrap(); + } + let metadata = get_models(calcite_ref); + let introspected = ParsedConfiguration { + version: Version::This, + _schema: args._schema, + model: args.model, + model_file_path: args.model_file_path, + fixes: args.fixes, + supports_json_object: args.supports_json_object, + jars: args.jars, + metadata: Some(metadata), + }; + Ok(introspected) +} + +/// Parse the configuration format from a directory. +pub async fn parse_configuration( + configuration_dir: impl AsRef, +) -> Result { + let configuration_file = configuration_dir.as_ref().join(CONFIGURATION_FILENAME); + + let configuration_file_contents = + fs::read_to_string(&configuration_file) + .await + .map_err(|err| { + ParseConfigurationError::IoErrorButStringified(format!( + "{}: {}", + &configuration_file.display(), + err + )) + })?; + + let parsed_config: ParsedConfiguration = serde_json::from_str(&configuration_file_contents) + .map_err(|error| ParseConfigurationError::ParseError { + file_path: configuration_file.clone(), + line: error.line(), + column: error.column(), + message: error.to_string(), + })?; + + Ok(parsed_config) +} + +/// Write the parsed configuration into a directory on disk. +pub async fn write_parsed_configuration( + parsed_config: ParsedConfiguration, + out_dir: impl AsRef, +) -> Result<(), WriteParsedConfigurationError> { + let configuration_file = out_dir.as_ref().to_owned().join(CONFIGURATION_FILENAME); + fs::create_dir_all(out_dir.as_ref()).await?; + + // create the configuration file + fs::write( + configuration_file, + serde_json::to_string_pretty(&parsed_config) + .map_err(|e| WriteParsedConfigurationError::IoError(e.into()))? + + "\n", + ) + .await?; + + // create the jsonschema file + let configuration_jsonschema_file_path = out_dir + .as_ref() + .to_owned() + .join(CONFIGURATION_JSONSCHEMA_FILENAME); + + let output = schemars::schema_for!(ParsedConfiguration); + fs::write( + &configuration_jsonschema_file_path, + serde_json::to_string_pretty(&output) + .map_err(|e| WriteParsedConfigurationError::IoError(e.into()))? + + "\n", + ) + .await?; + + Ok(()) +} diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 7e71e3b..cbe1278 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -8,14 +8,16 @@ license.workspace = true workspace = true [dependencies] +ndc-calcite-schema = { path = "../calcite-schema"} + anyhow = { workspace = true } clap = { workspace = true, features = ["derive", "env"] } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } serde_yaml = { workspace = true } -schemars = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true, features = ["full"] } +include_dir = { version = "0.7.4", features = ["default", "glob"] } [build-dependencies] build-data = { workspace = true } diff --git a/crates/cli/assets/.ddnignore b/crates/cli/assets/.ddnignore new file mode 100644 index 0000000..ed72dd1 --- /dev/null +++ b/crates/cli/assets/.ddnignore @@ -0,0 +1,2 @@ +.env* +compose.yaml diff --git a/config-templates/data/arrow/albums.arrow b/crates/cli/assets/data/arrow/albums.arrow similarity index 100% rename from config-templates/data/arrow/albums.arrow rename to crates/cli/assets/data/arrow/albums.arrow diff --git a/config-templates/data/arrow/artists.arrow b/crates/cli/assets/data/arrow/artists.arrow similarity index 100% rename from config-templates/data/arrow/artists.arrow rename to crates/cli/assets/data/arrow/artists.arrow diff --git a/config-templates/data/arrow/customers.arrow b/crates/cli/assets/data/arrow/customers.arrow similarity index 100% rename from config-templates/data/arrow/customers.arrow rename to crates/cli/assets/data/arrow/customers.arrow diff --git a/config-templates/data/arrow/employees.arrow b/crates/cli/assets/data/arrow/employees.arrow similarity index 100% rename from config-templates/data/arrow/employees.arrow rename to crates/cli/assets/data/arrow/employees.arrow diff --git a/config-templates/data/arrow/genres.arrow b/crates/cli/assets/data/arrow/genres.arrow similarity index 100% rename from config-templates/data/arrow/genres.arrow rename to crates/cli/assets/data/arrow/genres.arrow diff --git a/config-templates/data/arrow/invoice_items.arrow b/crates/cli/assets/data/arrow/invoice_items.arrow similarity index 100% rename from config-templates/data/arrow/invoice_items.arrow rename to crates/cli/assets/data/arrow/invoice_items.arrow diff --git a/config-templates/data/arrow/invoices.arrow b/crates/cli/assets/data/arrow/invoices.arrow similarity index 100% rename from config-templates/data/arrow/invoices.arrow rename to crates/cli/assets/data/arrow/invoices.arrow diff --git a/config-templates/data/arrow/media_types.arrow b/crates/cli/assets/data/arrow/media_types.arrow similarity index 100% rename from config-templates/data/arrow/media_types.arrow rename to crates/cli/assets/data/arrow/media_types.arrow diff --git a/config-templates/data/arrow/playlist_track.arrow b/crates/cli/assets/data/arrow/playlist_track.arrow similarity index 100% rename from config-templates/data/arrow/playlist_track.arrow rename to crates/cli/assets/data/arrow/playlist_track.arrow diff --git a/config-templates/data/arrow/playlists.arrow b/crates/cli/assets/data/arrow/playlists.arrow similarity index 100% rename from config-templates/data/arrow/playlists.arrow rename to crates/cli/assets/data/arrow/playlists.arrow diff --git a/config-templates/data/arrow/tracks.arrow b/crates/cli/assets/data/arrow/tracks.arrow similarity index 100% rename from config-templates/data/arrow/tracks.arrow rename to crates/cli/assets/data/arrow/tracks.arrow diff --git a/config-templates/data/chinook.db b/crates/cli/assets/data/chinook.db similarity index 100% rename from config-templates/data/chinook.db rename to crates/cli/assets/data/chinook.db diff --git a/config-templates/data/files/ARCHERS.json b/crates/cli/assets/data/files/ARCHERS.json similarity index 100% rename from config-templates/data/files/ARCHERS.json rename to crates/cli/assets/data/files/ARCHERS.json diff --git a/config-templates/data/files/DATES.csv b/crates/cli/assets/data/files/DATES.csv similarity index 100% rename from config-templates/data/files/DATES.csv rename to crates/cli/assets/data/files/DATES.csv diff --git a/config-templates/data/files/DEPTS.csv b/crates/cli/assets/data/files/DEPTS.csv similarity index 100% rename from config-templates/data/files/DEPTS.csv rename to crates/cli/assets/data/files/DEPTS.csv diff --git a/config-templates/data/files/EMPS.csv.gz b/crates/cli/assets/data/files/EMPS.csv.gz similarity index 100% rename from config-templates/data/files/EMPS.csv.gz rename to crates/cli/assets/data/files/EMPS.csv.gz diff --git a/config-templates/data/files/LONG_EMPS.csv b/crates/cli/assets/data/files/LONG_EMPS.csv similarity index 100% rename from config-templates/data/files/LONG_EMPS.csv rename to crates/cli/assets/data/files/LONG_EMPS.csv diff --git a/config-templates/data/files/SDEPTS.csv b/crates/cli/assets/data/files/SDEPTS.csv similarity index 100% rename from config-templates/data/files/SDEPTS.csv rename to crates/cli/assets/data/files/SDEPTS.csv diff --git a/config-templates/data/files/WACKY_COLUMN_NAMES.csv b/crates/cli/assets/data/files/WACKY_COLUMN_NAMES.csv similarity index 100% rename from config-templates/data/files/WACKY_COLUMN_NAMES.csv rename to crates/cli/assets/data/files/WACKY_COLUMN_NAMES.csv diff --git a/config-templates/models/model.arrow.json b/crates/cli/assets/models/model.arrow.json similarity index 86% rename from config-templates/models/model.arrow.json rename to crates/cli/assets/models/model.arrow.json index 7669e3a..539ed61 100644 --- a/config-templates/models/model.arrow.json +++ b/crates/cli/assets/models/model.arrow.json @@ -7,7 +7,7 @@ "type": "custom", "factory": "org.apache.calcite.adapter.arrow.ArrowSchemaFactory", "operand": { - "directory": "data/arrow" + "directory": "./data/arrow" } } ] diff --git a/crates/cli/assets/models/model.cassandra.json b/crates/cli/assets/models/model.cassandra.json new file mode 100644 index 0000000..ea9778a --- /dev/null +++ b/crates/cli/assets/models/model.cassandra.json @@ -0,0 +1,21 @@ +{ + "version": "1.0", + "defaultSchema": "cassandra", + "schemas": [ + { + "name": "cassandra", + "type": "custom", + "factory": "org.apache.calcite.adapter.cassandra.CassandraSchemaFactory", + "operand": { + "host": "", + "keyspace": "", + "username": "", + "password": ">", + "port": 9142, + "ssl": true, + "dc": "", + "pathToRootCert": "" + } + } + ] +} diff --git a/config-templates/models/model.files.json b/crates/cli/assets/models/model.files.json similarity index 82% rename from config-templates/models/model.files.json rename to crates/cli/assets/models/model.files.json index fc21294..59bb732 100644 --- a/config-templates/models/model.files.json +++ b/crates/cli/assets/models/model.files.json @@ -7,7 +7,7 @@ "type": "custom", "factory": "org.apache.calcite.adapter.file.FileSchemaFactory", "operand": { - "directory": "/etc/connector/data/files" + "directory": "./data/files" } } ] diff --git a/crates/cli/assets/models/model.h2.json b/crates/cli/assets/models/model.h2.json new file mode 100644 index 0000000..5a56810 --- /dev/null +++ b/crates/cli/assets/models/model.h2.json @@ -0,0 +1,15 @@ +{ + "version": "1.0", + "defaultSchema": "TEST", + "schemas": [ + { + "name": "TEST", + "type": "jdbc", + "jdbcDriver": "org.h2.Driver", + "jdbcUrl": "jdbc:h2:tcp://localhost:1521/test", + "jdbcSchema": "PUBLIC", + "jdbcUser": "sa", + "jdbcPassword": "" + } + ] +} diff --git a/crates/cli/assets/models/model.hive.json b/crates/cli/assets/models/model.hive.json new file mode 100644 index 0000000..e3febb2 --- /dev/null +++ b/crates/cli/assets/models/model.hive.json @@ -0,0 +1,12 @@ +{ + "version": "1.0", + "defaultSchema": "hive", + "schemas": [ + { + "name": "hive", + "type": "jdbc", + "jdbcUrl": "jdbc:hive2://192.168.86.233:10000", + "jdbcSchema": "default" + } + ] +} diff --git a/config-templates/models/model.json b/crates/cli/assets/models/model.json similarity index 77% rename from config-templates/models/model.json rename to crates/cli/assets/models/model.json index eb15a89..d567945 100644 --- a/config-templates/models/model.json +++ b/crates/cli/assets/models/model.json @@ -6,7 +6,7 @@ "name": "default", "type": "jdbc", "sqlDialectFactory": "org.kenstott.SQLiteSqlDialectFactory", - "jdbcUrl": "jdbc:sqlite:/etc/connector/data/chinook.db" + "jdbcUrl": "jdbc:sqlite:./data/chinook.db" } ] } \ No newline at end of file diff --git a/config-templates/models/model.pg.json b/crates/cli/assets/models/model.pg.json similarity index 100% rename from config-templates/models/model.pg.json rename to crates/cli/assets/models/model.pg.json diff --git a/crates/cli/assets/models/model.sqlite.json b/crates/cli/assets/models/model.sqlite.json new file mode 100644 index 0000000..d567945 --- /dev/null +++ b/crates/cli/assets/models/model.sqlite.json @@ -0,0 +1,12 @@ +{ + "version": "1.0", + "defaultSchema": "default", + "schemas": [ + { + "name": "default", + "type": "jdbc", + "sqlDialectFactory": "org.kenstott.SQLiteSqlDialectFactory", + "jdbcUrl": "jdbc:sqlite:./data/chinook.db" + } + ] + } \ No newline at end of file diff --git a/crates/cli/readme.md b/crates/cli/readme.md index e0196a4..088cb95 100644 --- a/crates/cli/readme.md +++ b/crates/cli/readme.md @@ -1,6 +1,6 @@ -# ndc-postgres-cli +# ndc-calcite-cli -ndc-postgres-cli is used to configure a deployment of ndc-postgres. +ndc-calcite-cli is used to configure a deployment of ndc-calcite. It is intended to be automatically downloaded and invoked via the Hasura CLI, as a plugin. ## Create a configuration @@ -10,13 +10,13 @@ Create a configuration in a new directory using the following commands: 1. Initialize a configuration: ```sh - CONNECTION_URI='' cargo run --bin ndc-postgres-cli -- --context='' initialize + CONNECTION_URI='' cargo run --bin ndc-calcite-cli -- --context='' initialize ``` 2. Update the configuration by introspecting the database: ```sh - CONNECTION_URI='' cargo run --bin ndc-postgres-cli -- --context='' update + CONNECTION_URI='' cargo run --bin ndc-calcite-cli -- --context='' update ``` ## Upgrade a configuration @@ -24,18 +24,18 @@ Create a configuration in a new directory using the following commands: An older configuration version (e.g. "v3") can be upgraded to a newer one using the `upgrade` command. ```sh -cargo run --bin ndc-postgres-cli -- upgrade --dir-from --dir-to +cargo run --bin ndc-calcite-cli -- upgrade --dir-from --dir-to ``` ## Native Operations -Native Operations can be listed, added, and deleted using ndc-postgres-cli: +Native Operations can be listed, added, and deleted using ndc-calcite-cli: ```sh -$ CONNECTION_URI='' cargo run --bin ndc-postgres-cli -- --context='' native-operation --help +$ CONNECTION_URI='' cargo run --bin ndc-calcite-cli -- --context='' native-operation --help Commands on Native Operations -Usage: hasura-ndc-postgres native-operation +Usage: hasura-ndc-calcite native-operation Commands: list List the existing Native Operations diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index c9bd816..752609d 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -3,19 +3,17 @@ //! The CLI can do a few things. This provides a central point where those things are routed and //! then done, making it easier to test this crate deterministically. -pub mod metadata; -pub mod environment; -pub mod configuration; -pub mod error; - - -use std::{env, io}; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use clap::Subcommand; +use include_dir::{DirEntry, include_dir}; +use include_dir::Dir; use tokio::fs; -use crate::environment::Environment; +use ndc_calcite_schema::configuration::{introspect, parse_configuration, ParsedConfiguration, upgrade_to_latest_version, write_parsed_configuration}; +use ndc_calcite_schema::environment::Environment; + +mod metadata; const UPDATE_ATTEMPTS: u8 = 3; @@ -32,7 +30,7 @@ pub enum Command { /// Initialize a configuration in the current (empty) directory. Initialize { #[arg(long)] - /// Whether to create the hasura ndc-calcite metadata. + /// Whether to create the hasura connector metadata. with_metadata: bool, }, /// Update the configuration by introspecting the database, using the configuration options. @@ -43,7 +41,7 @@ pub enum Command { dir_from: PathBuf, #[arg(long)] dir_to: PathBuf, - } + }, } /// The set of errors that can go wrong _in addition to_ generic I/O or parsing errors. @@ -63,41 +61,17 @@ pub async fn run(command: Command, context: Context) -> anyhow Ok(()) } -fn copy_files(input_dir: &str, output_dir: &str) -> io::Result<()> { - let input_path = Path::new(input_dir); - let output_path = Path::new(output_dir); - - if !output_path.exists() { - std::fs::create_dir_all(&output_path)?; - } +const MODELS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/assets"); - if input_path.is_dir() { - for entry in std::fs::read_dir(input_path)? { - let entry = entry?; - let entry_path = entry.path(); - let output_file_path = output_path.join(entry_path.file_name().unwrap()); - if entry_path.is_dir() { - copy_files(entry_path.to_str().unwrap(), output_file_path.to_str().unwrap())?; - } else if entry_path.is_file() { - std::fs::copy(entry_path, output_file_path)?; - } - } - } - Ok(()) -} - -fn is_running_in_container() -> bool { - Path::new("/.dockerenv").exists() || env::var("KUBERNETES_SERVICE_HOST").is_ok() -} - -/// Initialize an empty directory with an empty ndc-calcite configuration. +/// Initialize an empty directory with an empty connector configuration. /// /// An empty configuration contains default settings and options, and is expected to be filled with /// information such as the database connection string by the user, and later on metadata /// information via introspection. /// -/// Optionally, this can also create the ndc-calcite metadata, which is used by the Hasura CLI to +/// Optionally, this can also create the connector metadata, which is used by the Hasura CLI to /// automatically work with this CLI as a plugin. + async fn initialize(with_metadata: bool, context: Context) -> anyhow::Result<()> { // refuse to initialize the directory unless it is empty let mut items_in_dir = fs::read_dir(&context.context_path).await?; @@ -105,51 +79,56 @@ async fn initialize(with_metadata: bool, context: Context) -> Err(Error::DirectoryIsNotEmpty)?; } - let config_path = if is_running_in_container() { - Path::new("/config-templates") - } else { - Path::new("../config-templates") - }; - let context_path_str = context.context_path.to_str().ok_or(anyhow::anyhow!("Failed to convert PathBuf to &str"))?; - let config_path_str = config_path.to_str().ok_or(anyhow::anyhow!("Failed to convert PathBuf to &str"))?; - let _ = copy_files(config_path_str, context_path_str); - - configuration::write_parsed_configuration( - configuration::ParsedConfiguration::initial(), + write_parsed_configuration( + ParsedConfiguration::initial(), &context.context_path, ) - .await?; + .await?; + + for entry in MODELS_DIR.find("**/*").unwrap() { + match entry { + DirEntry::Dir(dir) => { + let path = dir.path(); + fs::create_dir(&context.context_path.join(path)).await? + } + DirEntry::File(file) => { + let path = file.path(); + let contents = file.contents(); + std::fs::write(&context.context_path.join(path), contents).expect("Unable to write file"); + } + } + } // if requested, create the metadata if with_metadata { - let metadata_dir = context.context_path.join(".hasura-ndc-calcite"); + let metadata_dir = context.context_path.join(".hasura-connector"); fs::create_dir(&metadata_dir).await?; - let metadata_file = metadata_dir.join("ndc-calcite-metadata.yaml"); + let metadata_file = metadata_dir.join("connector-metadata.yaml"); let metadata = metadata::ConnectorMetadataDefinition { packaging_definition: metadata::PackagingDefinition::PrebuiltDockerImage( metadata::PrebuiltDockerImagePackaging { docker_image: format!( - "docker.io/kstott/meta-connector:{}", + "docker.io/kstott/meta_connector:{}", context.release_version.unwrap_or("latest") ), }, ), supported_environment_variables: vec![metadata::EnvironmentVariableDefinition { name: "MODEL_FILE".to_string(), - description: "The Calcite connection model".to_string(), - default_value: Some("/etc/connection/models/model.json".to_string()), + description: "The calcite connection mode file path".to_string(), + default_value: Some("./models/model.json".to_string()), }], commands: metadata::Commands { update: Some("hasura-ndc-calcite update".to_string()), watch: None, }, cli_plugin: Some(metadata::CliPluginDefinition { - name: "ndc-calcite".to_string(), + name: "ndc-calcite-cli".to_string(), version: context.release_version.unwrap_or("latest").to_string(), }), docker_compose_watch: vec![metadata::DockerComposeWatchItem { path: "./".to_string(), - target: Some(".".to_string()), + target: Some("/etc/connector".to_string()), action: metadata::DockerComposeWatchAction::SyncAndRestart, ignore: vec![], }], @@ -163,26 +142,26 @@ async fn initialize(with_metadata: bool, context: Context) -> /// Update the configuration in the current directory by introspecting the database. /// -/// This expects a configuration with a valid connection URI. +/// This expects a configuration with a valid model.json. async fn update(context: Context) -> anyhow::Result<()> { // It is possible to change the file in the middle of introspection. // We want to detect this scenario and retry, or fail if we are unable to. // We do that with a few attempts. for _attempt in 1..=UPDATE_ATTEMPTS { let existing_configuration = - configuration::parse_configuration(&context.context_path).await?; + parse_configuration(&context.context_path).await?; let output = - configuration::introspect(existing_configuration.clone(), &context.environment).await?; + introspect(existing_configuration.clone(), &context.context_path, &context.environment).await?; // Check that the input file did not change since we started introspecting, let input_again_before_write = - configuration::parse_configuration(&context.context_path).await?; + parse_configuration(&context.context_path).await?; // and skip this attempt if it has. if input_again_before_write == existing_configuration { // In order to be sure to capture default values absent in the initial input we have to // always write out the updated configuration. - configuration::write_parsed_configuration(output, &context.context_path).await?; + write_parsed_configuration(output, &context.context_path).await?; return Ok(()); } @@ -199,9 +178,9 @@ async fn update(context: Context) -> anyhow::Result<()> { /// out to a different directory. /// async fn upgrade(dir_from: PathBuf, dir_to: PathBuf) -> anyhow::Result<()> { - let old_configuration = configuration::parse_configuration(dir_from).await?; - let upgraded_configuration = configuration::upgrade_to_latest_version(old_configuration); - configuration::write_parsed_configuration(upgraded_configuration, dir_to).await?; + let old_configuration = parse_configuration(dir_from).await?; + let upgraded_configuration = upgrade_to_latest_version(old_configuration); + write_parsed_configuration(upgraded_configuration, dir_to).await?; eprintln!("Upgrade completed successfully. You may need to also run 'update'."); diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 2de652f..2792676 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -8,9 +8,9 @@ use std::path::PathBuf; use std::process::ExitCode; use clap::Parser; -use ndc_calcite_cli::{Command, Context, run, environment::ProcessEnvironment}; - +use ndc_calcite_cli::*; +use ndc_calcite_schema as configuration; /// The release version specified at build time. /// @@ -54,7 +54,7 @@ async fn try_main() -> anyhow::Result<()> { }; let context = Context { context_path, - environment: ProcessEnvironment, + environment: configuration::environment::ProcessEnvironment, release_version: RELEASE_VERSION, }; run(args.subcommand, context).await?; diff --git a/crates/cli/src/metadata.rs b/crates/cli/src/metadata.rs index edc7965..74d2c8d 100644 --- a/crates/cli/src/metadata.rs +++ b/crates/cli/src/metadata.rs @@ -1,6 +1,6 @@ -//! Structures that represent the ndc-calcite metadata definition. +//! Structures that represent the connector metadata definition. //! -//! See . +//! See https://github.com/hasura/ndc-hub/blob/main/rfcs/0001-packaging.md#connector-definition. use serde::{Deserialize, Serialize}; diff --git a/crates/cli/tests/initialize_tests.rs b/crates/cli/tests/initialize_tests.rs index 821e410..045d50b 100644 --- a/crates/cli/tests/initialize_tests.rs +++ b/crates/cli/tests/initialize_tests.rs @@ -3,8 +3,8 @@ mod common; use tokio::fs; use ndc_calcite_cli::*; -use ndc_calcite_cli::configuration as configuration; -use ndc_calcite_cli::configuration::ParsedConfiguration; +use ndc_calcite_configuration as configuration; +use ndc_calcite_configuration::ParsedConfiguration; #[tokio::test] async fn test_initialize_directory() -> anyhow::Result<()> { @@ -35,8 +35,8 @@ async fn test_initialize_directory() -> anyhow::Result<()> { let metadata_file_path = dir .path() - .join(".hasura-ndc-calcite") - .join("ndc-calcite-metadata.yaml"); + .join(".hasura-connector") + .join("connector-metadata.yaml"); assert!(!metadata_file_path.exists()); Ok(()) @@ -133,8 +133,8 @@ async fn test_initialize_directory_with_metadata() -> anyhow::Result<()> { let metadata_file_path = dir .path() - .join(".hasura-ndc-calcite") - .join("ndc-calcite-metadata.yaml"); + .join(".hasura-connector") + .join("connector-metadata.yaml"); assert!(metadata_file_path.exists()); let contents = fs::read_to_string(metadata_file_path).await?; common::assert_ends_with_newline(&contents); @@ -170,8 +170,8 @@ async fn test_initialize_directory_with_metadata_and_release_version() -> anyhow let metadata_file_path = dir .path() - .join(".hasura-ndc-calcite") - .join("ndc-calcite-metadata.yaml"); + .join(".hasura-connector") + .join("connector-metadata.yaml"); assert!(metadata_file_path.exists()); let contents = fs::read_to_string(metadata_file_path).await?; common::assert_ends_with_newline(&contents); diff --git a/crates/cli/tests/snapshots/initialize_tests__initialize_directory_with_metadata.snap b/crates/cli/tests/snapshots/initialize_tests__initialize_directory_with_metadata.snap index e6642f9..cb28435 100644 --- a/crates/cli/tests/snapshots/initialize_tests__initialize_directory_with_metadata.snap +++ b/crates/cli/tests/snapshots/initialize_tests__initialize_directory_with_metadata.snap @@ -4,15 +4,15 @@ expression: contents --- packagingDefinition: type: PrebuiltDockerImage - dockerImage: ghcr.io/hasura/ndc-postgres:latest + dockerImage: docker.io/hasura/ndc-calcite:latest supportedEnvironmentVariables: -- name: CONNECTION_URI - description: The PostgreSQL connection URI - defaultValue: postgresql://read_only_user:readonlyuser@35.236.11.122:5432/v3-docs-sample-app +- name: MODEL_FILE + description: The Calcite model file path + defaultValue: ./models/model.json commands: - update: hasura-ndc-postgres update + update: hasura-ndc-calcite update cliPlugin: - name: ndc-postgres + name: ndc-calcite version: latest dockerComposeWatch: - path: ./ diff --git a/crates/cli/tests/snapshots/initialize_tests__initialize_directory_with_metadata_and_release_version.snap b/crates/cli/tests/snapshots/initialize_tests__initialize_directory_with_metadata_and_release_version.snap index 2940634..57f3c92 100644 --- a/crates/cli/tests/snapshots/initialize_tests__initialize_directory_with_metadata_and_release_version.snap +++ b/crates/cli/tests/snapshots/initialize_tests__initialize_directory_with_metadata_and_release_version.snap @@ -4,15 +4,15 @@ expression: contents --- packagingDefinition: type: PrebuiltDockerImage - dockerImage: ghcr.io/hasura/ndc-postgres:v1.2.3 + dockerImage: ghcr.io/hasura/ndc-calcite:v1.2.3 supportedEnvironmentVariables: - name: CONNECTION_URI - description: The PostgreSQL connection URI - defaultValue: postgresql://read_only_user:readonlyuser@35.236.11.122:5432/v3-docs-sample-app + description: The calciteQL connection URI + defaultValue: calciteql://read_only_user:readonlyuser@35.236.11.122:5432/v3-docs-sample-app commands: - update: hasura-ndc-postgres update + update: hasura-ndc-calcite update cliPlugin: - name: ndc-postgres + name: ndc-calcite version: v1.2.3 dockerComposeWatch: - path: ./ diff --git a/crates/connectors/ndc-calcite/Cargo.toml b/crates/connectors/ndc-calcite/Cargo.toml index cc80c6f..0c3ab07 100644 --- a/crates/connectors/ndc-calcite/Cargo.toml +++ b/crates/connectors/ndc-calcite/Cargo.toml @@ -22,38 +22,40 @@ native-tls = ["reqwest/native-tls"] rustls = ["reqwest/rustls"] [dependencies] -indexmap = "2.2.6" -jni = { version = "0.21.1", features = ["invocation"] } -ndc-models = { git = "http://github.com/hasura/ndc-spec.git", tag = "v0.1.4" } -ndc-sdk = { git = "https://github.com/hasura/ndc-sdk-rs.git", tag = "v0.1.4" } -async-trait = "0.1.79" -axum = { version = "0.6.20", features = ["http2"] } -axum-extra = "0.8.0" -bytes = "1.6.0" -clap = { version = "4.5.4", features = ["derive", "env"] } -http = "0.2" -mime = "0.3.17" -opentelemetry = "0.22.0" -opentelemetry-http = "0.11.0" -opentelemetry-otlp = { version = "0.15.0", features = ["reqwest-client", "gzip-tonic", "tls", "tls-roots", "http-proto"] } -opentelemetry-semantic-conventions = "0.14.0" -opentelemetry_sdk = { version = "0.22.1", features = ["rt-tokio"] } -opentelemetry-zipkin = "0.20.0" -prometheus = "0.13.3" -reqwest = "0.11.27" -serde = { version = "1.0.197", features = ["derive"] } -serde_json = { version = "1.0.115", features = ["raw_value"] } -thiserror = "1.0" -tokio = { version = "1.36.0", features = ["fs", "macros", "rt-multi-thread", "signal"] } -tower-http = { version = "0.4.4", features = ["cors", "trace", "validate-request"] } -tracing = "0.1.40" -tracing-opentelemetry = "0.23.0" -tracing-subscriber = { version = "0.3", default-features = false, features = ["ansi", "env-filter", "fmt", "json"] } -url = "2.5.0" -dotenv = "0.15.0" -once_cell = "1.19.0" -anyhow = "1.0.86" -log = "0.4.22" +ndc-calcite-schema = { path = "../../calcite-schema" } +ndc-calcite-values = { path = "../../values"} +indexmap = {workspace = true} +jni = { workspace = true } +ndc-models = { workspace = true } +ndc-sdk = { workspace = true } +async-trait = { workspace = true} +axum = { workspace = true, features = ["http2"] } +axum-extra = {workspace = true} +bytes = {workspace = true} +clap = { workspace = true, features = ["derive", "env"] } +http = { workspace = true } +mime = {workspace = true} +opentelemetry = {workspace = true} +opentelemetry-http = {workspace = true} +opentelemetry-otlp = { workspace = true, features = ["reqwest-client", "gzip-tonic", "tls", "tls-roots", "http-proto"] } +opentelemetry-semantic-conventions = {workspace = true} +opentelemetry_sdk = { workspace = true, features = ["rt-tokio"] } +opentelemetry-zipkin = {workspace = true} +prometheus = {workspace = true} +reqwest = {workspace = true} +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true, features = ["raw_value"] } +thiserror = {workspace = true} +tokio = { workspace = true, features = ["fs", "macros", "rt-multi-thread", "signal"] } +tower-http = { workspace = true, features = ["cors", "trace", "validate-request"] } +tracing = {workspace = true} +tracing-opentelemetry = { workspace = true } +tracing-subscriber = {workspace = true, default-features = false, features = ["ansi", "env-filter", "fmt", "json"] } +url = {workspace = true} +dotenv = {workspace = true} +once_cell = {workspace = true} +anyhow = {workspace = true} +log = {workspace = true} [dev-dependencies] -axum-test-helper = "0.3.0" \ No newline at end of file +axum-test-helper = {workspace = true} \ No newline at end of file diff --git a/crates/connectors/ndc-calcite/src/calcite.rs b/crates/connectors/ndc-calcite/src/calcite.rs index 547bbaf..c543033 100644 --- a/crates/connectors/ndc-calcite/src/calcite.rs +++ b/crates/connectors/ndc-calcite/src/calcite.rs @@ -3,53 +3,22 @@ //! Takes a Calcite query, passes it to Calcite Query Engine //! and then transforms it into a Vec. //! -use std::collections::HashMap; use std::fmt; use indexmap::IndexMap; use jni::JNIEnv; -use jni::objects::{GlobalRef, JObject, JString, JValueGen, JValueOwned}; +use jni::objects::{GlobalRef, JObject, JString, JValueGen}; use jni::objects::JValueGen::Object; use ndc_models as models; -use ndc_models::RowFieldValue; -use ndc_sdk::connector::{InitializationError, QueryError}; -use serde_json::{Value}; +use ndc_models::{FieldName, RowFieldValue}; +use ndc_sdk::connector::{QueryError}; +use serde_json::Value; use tracing::{event, Level}; +use ndc_calcite_schema::version5::ParsedConfiguration; +use ndc_calcite_schema::jvm::get_jvm; +use ndc_calcite_schema::version5::create_calcite_connection; -use crate::configuration::CalciteConfiguration; -use crate::jvm::get_jvm; -use crate::configuration::TableMetadata; - -pub type Row = IndexMap; - -#[tracing::instrument] -fn create_calcite_connection<'a>( - configuration: &CalciteConfiguration, - calcite_query: &JObject<'a>, - env: &'a mut JNIEnv<'a>, -) -> Result, InitializationError> { - let calcite_model = configuration.clone().model_file_path.unwrap_or_default(); - let arg0: JObject = env.new_string(calcite_model).unwrap().into(); - let args: &[JValueGen<&JObject<'_>>] = &[Object(&arg0)]; - let method_signature = "(Ljava/lang/String;)Ljava/sql/Connection;"; - let result = env.call_method( - calcite_query, - "createCalciteConnection", - method_signature, - args, - ); - - match result { - Ok(val) => { - event!(Level::INFO, "Connected to Calcite"); - Ok(val) - } - Err(e) => { - event!(Level::ERROR, "Error while connecting to Calcite: {:?}", e); - Err(InitializationError::Other(Box::new(e))) - } - } -} +pub type Row = IndexMap; /// Creates a Calcite query engine. /// @@ -72,11 +41,10 @@ fn create_calcite_connection<'a>( /// use jni::JNIEnv; /// use jni::objects::JObject; /// use tracing::Level; -/// -/// # fn create_calcite_connection(configuration: &CalciteConfiguration, instance: &JObject, env: &mut JNIEnv) { unimplemented!() } +/// use ndc_calcite_schema::version5::{create_calcite_connection, ParsedConfiguration}; /// /// #[tracing::instrument] -/// pub fn create_calcite_query_engine<'a>(configuration: &CalciteConfiguration, env: &'a mut JNIEnv<'a>) -> JObject<'a> { +/// pub fn create_calcite_query_engine<'a>(configuration: &ParsedConfiguration, env: &'a mut JNIEnv<'a>) -> JObject<'a> { /// let class = env.find_class("org/kenstott/CalciteQuery").unwrap(); /// let instance = env.new_object(class, "()V", &[]).unwrap(); /// let _ = create_calcite_connection(configuration, &instance, env); @@ -85,7 +53,7 @@ fn create_calcite_connection<'a>( /// } /// ``` #[tracing::instrument] -pub fn create_calcite_query_engine<'a>(configuration: &CalciteConfiguration, env: &'a mut JNIEnv<'a>) -> JObject<'a> { +pub fn create_calcite_query_engine<'a>(configuration: &ParsedConfiguration, env: &'a mut JNIEnv<'a>) -> JObject<'a> { let class = env.find_class("org/kenstott/CalciteQuery").unwrap(); let instance = env.new_object(class, "()V", &[]).unwrap(); let _ = create_calcite_connection(configuration, &instance, env); @@ -93,38 +61,6 @@ pub fn create_calcite_query_engine<'a>(configuration: &CalciteConfiguration, env return instance; } -/// Retrieves models from Calcite. -/// -/// # Arguments -/// -/// * `calcite_ref` - A reference to the Calcite instance. -/// -/// # Return -/// -/// A `HashMap` containing the retrieved models. The outer `HashMap` maps model names -/// to inner `HashMap`s, where each inner `HashMap` represents a model with its properties. -#[tracing::instrument] -pub fn get_models(calcite_ref: GlobalRef) -> HashMap { - let jvm = get_jvm().lock().unwrap(); - let env = jvm.attach_current_thread().unwrap(); - let calcite_query = env.new_local_ref(calcite_ref).unwrap(); - let mut env = jvm.attach_current_thread_as_daemon().unwrap(); - let args: &[JValueGen<&JObject<'_>>] = &[]; - let method_signature = "()Ljava/lang/String;"; - let result = env.call_method(calcite_query, "getModels", method_signature, args); - let map= match result.unwrap() { - Object(obj) => { - let j_string = JString::from(obj); - let json_string: String = env.get_string(&j_string).unwrap().into(); - let map: HashMap = serde_json::from_str(&json_string).unwrap(); - map - } - _ => todo!(), - }; - event!(Level::INFO, "Retrieved models from Calcite"); - return map; -} - fn parse_to_row(data: Vec) -> Vec { let mut rows: Vec = Vec::new(); for item in data { @@ -181,7 +117,7 @@ fn parse_to_row(data: Vec) -> Vec { // ANCHOR: calcite_query #[tracing::instrument] pub fn calcite_query( - config: &CalciteConfiguration, + config: &ParsedConfiguration, calcite_reference: GlobalRef, sql_query: &str, query_metadata: &models::Query, @@ -208,7 +144,7 @@ pub fn calcite_query( Ok(rows) => rows, Err(_) => { println!("{:?}", json_string); - return Err(QueryError::Other(Box::new(CalciteError{message: String::from("Invalid response from Calcite.")}))) + return Err(QueryError::Other(Box::new(CalciteError{message: String::from("Invalid response from Calcite.")}), serde_json::from_str(&json_string).unwrap_or_default())) } }; let rows = parse_to_row(json_rows); @@ -220,7 +156,7 @@ pub fn calcite_query( log_event(Level::INFO, &format!("Completed Query. Retrieved {} rows. Result: {}", rows.len().to_string(), serde_json::to_string(&rows).unwrap())); Ok(rows) }, - _ => Err(QueryError::Other(Box::new(CalciteError{message: String::from("Invalid response from Calcite.")}))) + _ => Err(QueryError::Other(Box::new(CalciteError{message: String::from("Invalid response from Calcite.")}), Value::Null)) } } @@ -238,7 +174,7 @@ fn fix_rows(rows: Vec, query_metadata: &models::Query) -> Vec { let fields = query_metadata.clone().fields.unwrap_or_default(); let aggregates = query_metadata.clone().aggregates.unwrap_or_default(); let max_keys = fields.len() + aggregates.len(); - let mut key_sample: Vec = vec![]; + let mut key_sample: Vec = vec![]; for (key, _) in fields { key_sample.push(key); @@ -252,7 +188,7 @@ fn fix_rows(rows: Vec, query_metadata: &models::Query) -> Vec { if max_keys > row.len() { for key in &key_sample { if !row.contains_key(key) { - row.insert(key.into(), RowFieldValue(Value::Null)); + row.insert(key.clone(), RowFieldValue(Value::Null)); } } } diff --git a/crates/connectors/ndc-calcite/src/connector/calcite.rs b/crates/connectors/ndc-calcite/src/connector/calcite.rs index 603ca6a..0d10cd8 100644 --- a/crates/connectors/ndc-calcite/src/connector/calcite.rs +++ b/crates/connectors/ndc-calcite/src/connector/calcite.rs @@ -4,31 +4,34 @@ //! the request to the underlying code and providing the result. //! use std::collections::BTreeMap; -use std::{env, fs}; +use std::{fs}; use std::path::Path; use log::{info, error}; use async_trait::async_trait; use dotenv; use jni::objects::GlobalRef; -use ndc_models::{Argument, ExplainResponse, RelationshipArgument}; +use ndc_models as models; +use ndc_models::{ArgumentName, Capabilities, CollectionName, Relationship, RelationshipName, VariableName}; use ndc_sdk::connector::{ Connector, ConnectorSetup, ExplainError, FetchMetricsError, HealthError, InitializationError, MutationError, ParseError, QueryError, SchemaError, }; use ndc_sdk::json_response::JsonResponse; -use ndc_sdk::models; +use serde_json::Value; use tracing::info_span; use tracing::Instrument; -use crate::{calcite, jvm, query, schema}; use crate::capabilities::calcite_capabilities; -use crate::configuration::{CalciteConfiguration, Model}; -use crate::jvm::init_jvm; +use ndc_calcite_schema::jvm::{get_jvm, init_jvm}; +use ndc_calcite_schema::calcite::Model; +use ndc_calcite_schema::models::get_models; +use ndc_calcite_schema::schema::get_schema as retrieve_schema; +use ndc_calcite_schema::version5::ParsedConfiguration; +use ndc_calcite_values::is_running_in_container::is_running_in_container; +use ndc_calcite_values::values::{CONFIG_FILE_NAME, DEV_CONFIG_FILE_NAME}; +use crate::{calcite, query}; use crate::query::QueryParams; -pub const CONFIG_FILE_NAME: &str = "configuration.json"; -pub const DEV_CONFIG_FILE_NAME: &str = "dev.local.configuration.json"; - #[derive(Clone, Default, Debug)] pub struct Calcite {} @@ -37,39 +40,14 @@ pub struct CalciteState { pub calcite_ref: GlobalRef, } -/// Checks if the code is running inside a container. -/// -/// This function checks for the existence of the `/.dockerenv` file in the filesystem, -/// which is commonly used to indicate that the code is running inside a Docker container. -/// -/// # Examples -/// -/// ``` -/// use std::path::Path; -/// -/// fn is_running_in_container() -> bool { -/// Path::new("/.dockerenv").exists() -/// } -/// -/// assert_eq!(is_running_in_container(), false); -/// ``` -/// -/// # Returns -/// -/// Returns `true` if the code is running inside a container, `false` otherwise. -#[tracing::instrument] -pub fn is_running_in_container() -> bool { - Path::new("/.dockerenv").exists() || env::var("KUBERNETES_SERVICE_HOST").is_ok() -} - #[tracing::instrument] fn execute_query_with_variables( - config: &CalciteConfiguration, - coll: &str, - args: &BTreeMap, - coll_rel: &BTreeMap, + config: &ParsedConfiguration, + coll: &CollectionName, + args: &BTreeMap, + coll_rel: &BTreeMap, query: &models::Query, - vars: &BTreeMap, + vars: &BTreeMap, state: &CalciteState, explain: &bool ) -> Result { @@ -99,7 +77,7 @@ impl ConnectorSetup for Calcite { match fs::read_to_string(file_path) { Ok(file_content) => { println!("Configuration file content: {:?}", file_content); - let mut json_object: CalciteConfiguration = serde_json::from_str(&file_content) + let mut json_object: ParsedConfiguration = serde_json::from_str(&file_content) .map_err(|err| ParseError::Other(Box::from(err.to_string())))?; match json_object.model_file_path { None => { @@ -123,7 +101,7 @@ impl ConnectorSetup for Calcite { match json_object.metadata { None => { let state = init_state(&json_object).expect("TODO: panic message"); - json_object.metadata = Some(calcite::get_models(state.calcite_ref)); + json_object.metadata = Some(get_models(state.calcite_ref)); println!("metadata: {:?}", serde_json::to_string_pretty(&json_object.metadata)); } Some(_) => {} @@ -146,7 +124,7 @@ impl ConnectorSetup for Calcite { #[async_trait] impl Connector for Calcite { - type Configuration = CalciteConfiguration; + type Configuration = ParsedConfiguration; type State = CalciteState; fn fetch_metrics( @@ -163,8 +141,8 @@ impl Connector for Calcite { Ok(()) } - async fn get_capabilities() -> JsonResponse { - calcite_capabilities().into() + async fn get_capabilities() -> Capabilities { + calcite_capabilities().capabilities } async fn get_schema( @@ -179,17 +157,17 @@ impl Connector for Calcite { let calcite; let calcite_ref; { - let java_vm = jvm::get_jvm().lock().unwrap(); + let java_vm = get_jvm().lock().unwrap(); let mut env = java_vm.attach_current_thread_as_daemon().unwrap(); calcite = calcite::create_calcite_query_engine(configuration, &mut env); let env = java_vm.attach_current_thread_as_daemon().unwrap(); calcite_ref = env.new_global_ref(calcite).unwrap(); } - let schema = schema::get_schema(configuration, calcite_ref.clone()); + let schema = retrieve_schema(configuration, calcite_ref.clone()); match schema { - Ok(schema) => Ok(schema.into()), - Err(e) => Err(SchemaError::Other(e.to_string().into())), + Ok(schema) => Ok(JsonResponse::from(schema)), + Err(e) => Err(SchemaError::Other(e.to_string().into(), Value::Null)), } } @@ -200,8 +178,8 @@ impl Connector for Calcite { ) -> Result, ExplainError> { let variable_sets = request.variables.unwrap_or(vec![BTreeMap::new()]); let mut map: BTreeMap = BTreeMap::new(); - let input_map: BTreeMap = request.arguments.clone(); - let relationship_arguments : BTreeMap = + let input_map: BTreeMap = request.arguments.clone(); + let relationship_arguments : BTreeMap = input_map.iter() .map(|(key, value)| (key.clone(), convert_to_relationship_argument(value)) @@ -217,12 +195,12 @@ impl Connector for Calcite { variables, &state, &true - ).map_err(|error| ExplainError::Other(Box::new(error)))?; + ).map_err(|error| ExplainError::Other(Box::new(error), Value::Null))?; match row_set.aggregates { None => {} Some(map_index) => { let map_btree: BTreeMap = map_index.iter() - .map(|(key, value)| (key.clone(), value.to_string())) + .map(|(key, value)| (key.clone().to_string(), value.to_string())) .collect(); map.extend(map_btree); } @@ -232,14 +210,14 @@ impl Connector for Calcite { Some(r) => { for map_index in r { let map_btree: BTreeMap = map_index.iter() - .map(|(key, value)| (key.clone(), value.0.to_string())) + .map(|(key, value)| (key.clone().to_string(), value.0.to_string())) .collect(); map.extend(map_btree); } } } } - let explain_response = ExplainResponse { + let explain_response = models::ExplainResponse { details: map, }; Ok(JsonResponse::from(explain_response)) @@ -269,8 +247,8 @@ impl Connector for Calcite { // println!("{:?}", serde_json::to_string_pretty(&request)); let variable_sets = request.variables.unwrap_or(vec![BTreeMap::new()]); let mut row_sets = vec![]; - let input_map: BTreeMap = request.arguments.clone(); - let relationship_arguments : BTreeMap = + let input_map: BTreeMap = request.arguments.clone(); + let relationship_arguments : BTreeMap = input_map.iter() .map(|(key, value)| // Assuming we have a function `convert_to_relationship_argument` @@ -308,19 +286,19 @@ impl Connector for Calcite { } } -fn convert_to_relationship_argument(p0: &Argument) -> RelationshipArgument { +fn convert_to_relationship_argument(p0: &models::Argument) -> models::RelationshipArgument { match p0 { - Argument::Variable { name } => RelationshipArgument::Variable { name: name.to_string() }, - Argument::Literal { value } => RelationshipArgument::Literal { value: value.clone() } + models::Argument::Variable { name } => models::RelationshipArgument::Variable { name: name.clone() }, + models::Argument::Literal { value } => models::RelationshipArgument::Literal { value: value.clone() } } } fn init_state( - configuration: &CalciteConfiguration, + configuration: &ParsedConfiguration, ) -> Result { dotenv::dotenv().ok(); - init_jvm(configuration); - let java_vm = jvm::get_jvm().lock().unwrap(); + init_jvm(&ndc_calcite_schema::configuration::ParsedConfiguration::Version5(configuration.clone())); + let java_vm = get_jvm().lock().unwrap(); let calcite; let calcite_ref; { diff --git a/crates/connectors/ndc-calcite/src/lib.rs b/crates/connectors/ndc-calcite/src/lib.rs index 29b6fcf..4f7ce0c 100644 --- a/crates/connectors/ndc-calcite/src/lib.rs +++ b/crates/connectors/ndc-calcite/src/lib.rs @@ -70,18 +70,12 @@ //! - Path'ed where-predicates (you can only use the root in List arguments) //! - Nested objects are not supported -pub mod aggregates; pub mod calcite; pub mod capabilities; -pub mod collections; -pub mod comparators; -pub mod jvm; -pub mod scalars; -pub mod schema; pub mod sql; -pub mod configuration; pub mod query; pub mod connector { pub mod calcite; } + diff --git a/crates/connectors/ndc-calcite/src/query.rs b/crates/connectors/ndc-calcite/src/query.rs index 0bff453..bb7a933 100644 --- a/crates/connectors/ndc-calcite/src/query.rs +++ b/crates/connectors/ndc-calcite/src/query.rs @@ -8,13 +8,13 @@ use std::collections::BTreeMap; use indexmap::IndexMap; -use ndc_models::{ComparisonTarget, ComparisonValue, Expression, Field, Query, Relationship, RelationshipArgument, RelationshipType, RowFieldValue}; +use ndc_models::{ArgumentName, CollectionName, ComparisonOperatorName, ComparisonTarget, ComparisonValue, Expression, Field, FieldName, Query, Relationship, RelationshipArgument, RelationshipName, RelationshipType, RowFieldValue, VariableName}; use ndc_sdk::connector::QueryError; use ndc_sdk::models; use serde_json::{Number, Value}; use crate::calcite::{calcite_query, Row}; -use crate::configuration::CalciteConfiguration; +use ndc_calcite_schema::version5::ParsedConfiguration; use crate::connector::calcite::CalciteState; use crate::sql; @@ -29,12 +29,12 @@ use crate::sql; /// `'a` - A lifetime parameter specifying the lifetime of the query parameters. #[derive(Clone, Copy)] pub struct QueryParams<'a> { - pub config: &'a CalciteConfiguration, - pub coll: &'a str, - pub coll_rel: &'a BTreeMap, - pub args: &'a BTreeMap, - pub query: &'a models::Query, - pub vars: &'a BTreeMap, + pub config: &'a ParsedConfiguration, + pub coll: &'a CollectionName, + pub coll_rel: &'a BTreeMap, + pub args: &'a BTreeMap, + pub query: &'a Query, + pub vars: &'a BTreeMap, pub state: &'a CalciteState, pub explain: &'a bool } @@ -45,7 +45,7 @@ pub struct QueryParams<'a> { /// These components include the argument values, the SELECT clause, the ORDER BY clause, /// the pagination settings, the aggregates, the predicates, the final aggregates, and the join clause. pub struct QueryComponents { - pub argument_values: BTreeMap, + pub argument_values: BTreeMap, pub select: Option, pub order_by: Option, pub pagination: Option, @@ -87,7 +87,7 @@ pub fn orchestrate_query( ) -> Result { let components = sql::parse_query(¶ms.config, params.coll, params.coll_rel, params.args, params.query, params.vars)?; let mut rows: Option> = process_rows(params, &components)?; - let aggregates: Option> = process_aggregates(params, &components)?; + let aggregates: Option> = process_aggregates(params, &components)?; let fields = params.query.clone().fields.unwrap_or_default(); for (field_name, field) in &fields { match &rows { @@ -134,12 +134,12 @@ fn generate_value_from_rows(rows: &Vec, sub_relationship: &Relationship) -> Ok(value) } -fn parse_relationship(sub_relationship: &Relationship) -> Result<(Vec<&String>, Vec<&String>, RelationshipType), QueryError> { - let pks: Vec<&String> = sub_relationship.column_mapping.values().collect(); +fn parse_relationship(sub_relationship: &Relationship) -> Result<(Vec<&FieldName>, Vec<&FieldName>, RelationshipType), QueryError> { + let pks: Vec<&FieldName> = sub_relationship.column_mapping.values().collect(); if pks.len() > 1 { - return Err(QueryError::Other(Box::from("Cannot create a sub-query based on a composite key"))); + return Err(QueryError::Other(Box::from("Cannot create a sub-query based on a composite key"), Value::Null)); } - let fks: Vec<&String> = sub_relationship.column_mapping.keys().collect(); + let fks: Vec<&FieldName> = sub_relationship.column_mapping.keys().collect(); let relationship_type = sub_relationship.relationship_type.clone(); Ok((pks, fks, relationship_type)) } @@ -168,7 +168,7 @@ fn process_rows(params: QueryParams, query_components: &QueryComponents) -> Resu } -fn process_aggregates(params: QueryParams, query_components: &QueryComponents) -> Result>, QueryError> { +fn process_aggregates(params: QueryParams, query_components: &QueryComponents) -> Result>, QueryError> { if let Some(phrase) = &query_components.aggregates { if phrase.is_empty() { return Ok(None); @@ -195,7 +195,7 @@ fn process_aggregates(params: QueryParams, query_components: &QueryComponents) - row.insert(key.into(), RowFieldValue(Value::from(Number::from(0)))); } } - let map: IndexMap = row.into_iter() + let map: IndexMap = row.into_iter() .map(|(k, v)| (k, v.0)) .collect(); Ok(Some(map)) @@ -207,19 +207,19 @@ fn process_aggregates(params: QueryParams, query_components: &QueryComponents) - } } -fn generate_predicate(pks: &Vec<&String>, value: Value) -> Result { +fn generate_predicate(pks: &Vec<&FieldName>, value: Value) -> Result { Ok(Expression::BinaryComparisonOperator { column: ComparisonTarget::Column { - name: pks[0].to_string(), + name: pks[0].clone(), field_path: None, path: vec![] }, - operator: "_in".to_string(), + operator: ComparisonOperatorName::from("_in".to_string()), value: ComparisonValue::Scalar { value }, }) } -fn revise_query(query: Box, predicate: Expression, pks: &Vec<&String>) -> Result, QueryError> { +fn revise_query(query: Box, predicate: Expression, pks: &Vec<&FieldName>) -> Result, QueryError> { let mut revised_query = query.clone(); revised_query.predicate = Some(predicate); revised_query.offset = None; @@ -227,8 +227,8 @@ fn revise_query(query: Box, predicate: Expression, pks: &Vec<&String>) -> let mut fields = query.fields.unwrap(); for pk in pks { if !fields.contains_key(*pk) { - fields.insert(pk.to_string(), Field::Column { - column: pk.to_string(), + fields.insert(FieldName::from(pk.to_string()), Field::Column { + column: FieldName::from(pk.to_string()), fields: None, arguments: Default::default(), }); @@ -238,7 +238,7 @@ fn revise_query(query: Box, predicate: Expression, pks: &Vec<&String>) -> Ok(revised_query) } -fn execute_query(params: QueryParams, arguments: &BTreeMap, sub_relationship: &Relationship, revised_query: &Query) -> Result, QueryError> { +fn execute_query(params: QueryParams, arguments: &BTreeMap, sub_relationship: &Relationship, revised_query: &Query) -> Result, QueryError> { let fk_rows = orchestrate_query(QueryParams { config: params.config, coll: &sub_relationship.target_collection, @@ -252,7 +252,7 @@ fn execute_query(params: QueryParams, arguments: &BTreeMap, field_name: &String, fk_rows: &Vec, pks: &Vec<&String>, fks: &Vec<&String>) -> Result>, QueryError> { +fn process_object_relationship(rows: Vec, field_name: &FieldName, fk_rows: &Vec, pks: &Vec<&FieldName>, fks: &Vec<&FieldName>) -> Result>, QueryError> { let modified_rows: Vec = rows.clone().into_iter().map(|mut row| { let pk_value = row.get(fks[0]).unwrap().0.clone(); let rowset = serde_json::map::Map::new(); @@ -270,7 +270,7 @@ fn process_object_relationship(rows: Vec, field_name: &String, fk_rows: &Ve Ok(Some(modified_rows)) } -fn process_array_relationship(rows: Option>, field_name: &String, fk_rows: &Vec, pks: &Vec<&String>, fks: &Vec<&String>, query: &Query) -> Result>, QueryError> { +fn process_array_relationship(rows: Option>, field_name: &FieldName, fk_rows: &Vec, pks: &Vec<&FieldName>, fks: &Vec<&FieldName>, query: &Query) -> Result>, QueryError> { let modified_rows: Vec = rows.clone().unwrap().into_iter().map(|mut row| { let pk_value = row.get(fks[0]).unwrap().0.clone(); let rowset = serde_json::map::Map::new(); diff --git a/crates/connectors/ndc-calcite/src/sql.rs b/crates/connectors/ndc-calcite/src/sql.rs index 5e9aeb0..8e00014 100644 --- a/crates/connectors/ndc-calcite/src/sql.rs +++ b/crates/connectors/ndc-calcite/src/sql.rs @@ -4,14 +4,14 @@ //! use std::collections::{BTreeMap, HashMap}; use std::error::Error; -use ndc_models::{Aggregate, ComparisonTarget, ComparisonValue, ExistsInCollection, Expression, Field, Query, Relationship, RelationshipArgument, UnaryComparisonOperator}; +use ndc_models::{Aggregate, ArgumentName, CollectionName, ComparisonOperatorName, ComparisonTarget, ComparisonValue, ErrorResponse, ExistsInCollection, Expression, Field, FieldName, Query, Relationship, RelationshipArgument, RelationshipName, UnaryComparisonOperator, VariableName}; use ndc_sdk::connector::QueryError; use ndc_sdk::models; use serde_json::{Value}; use tracing::{event, Level}; -use crate::configuration::CalciteConfiguration; -use crate::configuration::TableMetadata; +use ndc_calcite_schema::version5::ParsedConfiguration; +use ndc_calcite_schema::calcite::TableMetadata; use crate::query::QueryComponents; #[derive(Debug)] @@ -27,13 +27,13 @@ impl Error for VariableNotFoundError {} #[tracing::instrument] fn eval_argument( - variables: &BTreeMap, - argument: &models::RelationshipArgument, + variables: &BTreeMap, + argument: &RelationshipArgument, ) -> Result { match argument { RelationshipArgument::Variable { name } => variables .get(name.as_str()) - .ok_or(QueryError::Other(Box::new(VariableNotFoundError))) + .ok_or(QueryError::Other(Box::new(VariableNotFoundError), Value::String(name.to_string()))) .map(|val| val.clone()), RelationshipArgument::Literal { value } => Ok(value.clone()), RelationshipArgument::Column { .. } => { todo!() } @@ -42,11 +42,11 @@ fn eval_argument( #[tracing::instrument] fn select( - configuration: &CalciteConfiguration, - variables: &BTreeMap, - collection: &str, - query: &models::Query, - collection_relationships: &BTreeMap, + configuration: &ParsedConfiguration, + variables: &BTreeMap, + collection: &CollectionName, + query: &Query, + collection_relationships: &BTreeMap, prepend: Option, ) -> (Vec, Vec) { let mut field_statements: Vec = vec![]; @@ -96,7 +96,7 @@ fn select( } #[tracing::instrument] -fn order_by(query: &models::Query) -> Vec { +fn order_by(query: &Query) -> Vec { let mut order_statements: Vec = Vec::new(); match &query.order_by { Some(order) => { @@ -111,7 +111,7 @@ fn order_by(query: &models::Query) -> Vec { name, field_path, .. } => { let field_path = field_path.clone().unwrap_or_default(); - let mut p: Vec = vec![name.clone()]; + let mut p: Vec = vec![name.clone()]; p.extend(field_path); order_statements.push(format!("\"{}\" {}", p.join("."), order_direction)); } @@ -126,7 +126,7 @@ fn order_by(query: &models::Query) -> Vec { } #[tracing::instrument] -fn pagination(query: &models::Query) -> Vec { +fn pagination(query: &Query) -> Vec { let mut pagination_statements: Vec = Vec::new(); if query.limit.is_some() { pagination_statements.push(format!(" LIMIT {}", query.limit.unwrap())); @@ -141,9 +141,9 @@ fn pagination(query: &models::Query) -> Vec { } #[tracing::instrument] -fn create_column_name(calcite_configuration: &CalciteConfiguration, name: &str, field_path: &Option>) -> String { +fn create_column_name(calcite_configuration: &ParsedConfiguration, name: &FieldName, field_path: &Option>) -> String { match field_path { - None => name.into(), + None => name.to_string(), Some(f) => { format!("{}{}", f.join("."), name) } @@ -151,7 +151,7 @@ fn create_column_name(calcite_configuration: &CalciteConfiguration, name: &str, } #[tracing::instrument] -fn aggregates(configuration: &CalciteConfiguration, query: &models::Query) -> Vec { +fn aggregates(configuration: &ParsedConfiguration, query: &Query) -> Vec { let mut aggregates: Vec = Vec::new(); match &query.aggregates { None => {} @@ -217,21 +217,21 @@ fn aggregates(configuration: &CalciteConfiguration, query: &models::Query) -> Ve #[tracing::instrument] fn predicates( - configuration: &CalciteConfiguration, - collection: &str, - collection_relationships: &BTreeMap, - variables: &BTreeMap, - query: &models::Query, + configuration: &ParsedConfiguration, + collection: &CollectionName, + collection_relationships: &BTreeMap, + variables: &BTreeMap, + query: &Query, ) -> Result> { process_expression_option(configuration, collection, collection_relationships, variables, query.clone().predicate) } #[tracing::instrument] fn process_expression_option( - configuration: &CalciteConfiguration, - collection: &str, - collection_relationships: &BTreeMap, - variables: &BTreeMap, + configuration: &ParsedConfiguration, + collection: &CollectionName, + collection_relationships: &BTreeMap, + variables: &BTreeMap, predicate: Option, ) -> Result> { match predicate { @@ -258,16 +258,16 @@ fn sql_quotes(input: &str) -> String { #[tracing::instrument] fn process_sql_expression( - configuration: &CalciteConfiguration, - collection: &str, - collection_relationships: &BTreeMap, - variables: &BTreeMap, + configuration: &ParsedConfiguration, + collection: &CollectionName, + collection_relationships: &BTreeMap, + variables: &BTreeMap, expr: &Expression, ) -> Result> { let table = create_qualified_table_name( configuration.clone().metadata.unwrap().get(collection).unwrap() ); - let operation_tuples: Vec<(String, String)> = vec![ + let operation_tuples: Vec<(ComparisonOperatorName, String)> = vec![ ("_gt".into(), ">".into()), ("_lt".into(), "<".into()), ("_gte".into(), ">=".into()), @@ -409,7 +409,7 @@ fn process_sql_expression( } } -fn create_arguments(variables: &BTreeMap, arguments: &BTreeMap) -> Vec { +fn create_arguments(variables: &BTreeMap, arguments: &BTreeMap) -> Vec { let arguments: Vec = arguments.iter().map(|(name, arg)| { let value = match arg { RelationshipArgument::Variable { name } => variables.get(name).unwrap().to_string(), @@ -442,9 +442,9 @@ fn create_qualified_table_name(table_metadata: &TableMetadata) -> String { #[tracing::instrument] pub fn query_collection( - configuration: &CalciteConfiguration, - collection_name: &str, - _arguments: &BTreeMap, + configuration: &ParsedConfiguration, + collection_name: &CollectionName, + _arguments: &BTreeMap, select: Option, order_by: Option, pagination: Option, @@ -531,7 +531,7 @@ pub fn query_collection( } } -pub(crate) fn parse_query<'a>(configuration: &'a CalciteConfiguration, collection: &'a str, collection_relationships: &'a BTreeMap, arguments: &'a BTreeMap, query: &'a Query, variables: &'a BTreeMap) -> Result { +pub(crate) fn parse_query<'a>(configuration: &'a ParsedConfiguration, collection: &'a CollectionName, collection_relationships: &'a BTreeMap, arguments: &'a BTreeMap, query: &'a Query, variables: &'a BTreeMap) -> Result { let mut argument_values = BTreeMap::new(); for (argument_name, argument_value) in arguments { if argument_values @@ -541,10 +541,12 @@ pub(crate) fn parse_query<'a>(configuration: &'a CalciteConfiguration, collectio ) .is_some() { - return Err(QueryError::InvalidRequest(format!( + return Err(QueryError::InvalidRequest(ErrorResponse { message: format!( "Duplicate argument: {}", argument_name - ))); + ), + details: Value::String(argument_name.to_string()), + })); } } let (select_clause, join_clause) = select(configuration, variables, collection, query, collection_relationships, None); @@ -557,7 +559,10 @@ pub(crate) fn parse_query<'a>(configuration: &'a CalciteConfiguration, collectio let predicates: Option = match predicates { Ok(p) => Some(p), Err(e) => { - return Err(QueryError::InvalidRequest(e.to_string())); + return Err(QueryError::InvalidRequest(ErrorResponse { + message: e.to_string(), + details: Default::default() + })); } }; let final_aggregates = aggregates.clone().unwrap_or_default(); diff --git a/crates/values/Cargo.toml b/crates/values/Cargo.toml new file mode 100644 index 0000000..79076ed --- /dev/null +++ b/crates/values/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "ndc-calcite-values" +version.workspace = true +edition.workspace = true +license.workspace = true + +[lints] +workspace = true + +[dependencies] +ndc-models = { workspace = true } +jni = { version = "0.21.1", features = ["invocation"] } + +anyhow = { workspace = true } +# We only use clap for the derive feature. +clap = { workspace = true, features = ["derive", "env"] } +prometheus = {workspace = true } +schemars = { workspace = true, features = ["smol_str", "preserve_order"] } +serde = { workspace = true } +serde_json = { workspace = true, features = ["raw_value"] } +smol_str = { workspace = true } +sqlx = { workspace = true, features = ["json", "postgres", "runtime-tokio-rustls"] } +thiserror = { workspace = true } +tokio = { workspace = true, features = ["full"] } +tracing = { workspace = true } +once_cell = "1.19.0" + +[dev-dependencies] +jsonschema = { workspace = true } diff --git a/crates/values/src/is_running_in_container.rs b/crates/values/src/is_running_in_container.rs new file mode 100644 index 0000000..68676c7 --- /dev/null +++ b/crates/values/src/is_running_in_container.rs @@ -0,0 +1,27 @@ +use std::env; +use std::path::Path; + +/// Checks if the code is running inside a container. +/// +/// This function checks for the existence of the `/.dockerenv` file in the filesystem, +/// which is commonly used to indicate that the code is running inside a Docker container. +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// +/// fn is_running_in_container() -> bool { +/// Path::new("/.dockerenv").exists() +/// } +/// +/// assert_eq!(is_running_in_container(), false); +/// ``` +/// +/// # Returns +/// +/// Returns `true` if the code is running inside a container, `false` otherwise. +#[tracing::instrument] +pub fn is_running_in_container() -> bool { + Path::new("/.dockerenv").exists() || env::var("KUBERNETES_SERVICE_HOST").is_ok() +} \ No newline at end of file diff --git a/crates/values/src/lib.rs b/crates/values/src/lib.rs new file mode 100644 index 0000000..46373e1 --- /dev/null +++ b/crates/values/src/lib.rs @@ -0,0 +1,2 @@ +pub mod values; +pub mod is_running_in_container; diff --git a/crates/values/src/values.rs b/crates/values/src/values.rs new file mode 100644 index 0000000..1491731 --- /dev/null +++ b/crates/values/src/values.rs @@ -0,0 +1,4 @@ +pub const CONFIG_FILE_NAME: &str = "configuration.json"; +pub const DEV_CONFIG_FILE_NAME: &str = "dev.local.configuration.json"; +pub const CONFIGURATION_FILENAME: &str = "configuration.json"; +pub const CONFIGURATION_JSONSCHEMA_FILENAME: &str = "schema.json"; \ No newline at end of file