From 64fdc6b0168e4eee56d7ff1b06949e1d1e9174c3 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Wed, 27 Nov 2024 10:20:52 -0500 Subject: [PATCH 01/10] clean slate --- .gitignore | 14 - Cargo.lock | 4479 ----------------- Cargo.toml | 2 +- optd-cost-model/Cargo.lock | 4413 ---------------- optd-cost-model/Cargo.toml | 26 - optd-cost-model/src/common/mod.rs | 5 - optd-cost-model/src/common/nodes.rs | 127 - .../src/common/predicates/attr_index_pred.rs | 42 - .../src/common/predicates/bin_op_pred.rs | 87 - .../src/common/predicates/cast_pred.rs | 49 - .../src/common/predicates/constant_pred.rs | 220 - .../src/common/predicates/data_type_pred.rs | 40 - .../src/common/predicates/func_pred.rs | 23 - .../src/common/predicates/in_list_pred.rs | 48 - .../src/common/predicates/like_pred.rs | 66 - .../src/common/predicates/list_pred.rs | 47 - .../src/common/predicates/log_op_pred.rs | 85 - optd-cost-model/src/common/predicates/mod.rs | 12 - .../src/common/predicates/sort_order_pred.rs | 14 - .../src/common/predicates/un_op_pred.rs | 57 - .../src/common/properties/attr_ref.rs | 249 - optd-cost-model/src/common/properties/mod.rs | 41 - .../src/common/properties/schema.rs | 41 - optd-cost-model/src/common/types.rs | 84 - optd-cost-model/src/common/values.rs | 205 - optd-cost-model/src/cost/agg.rs | 198 - optd-cost-model/src/cost/filter/attribute.rs | 182 - optd-cost-model/src/cost/filter/comp_op.rs | 280 -- optd-cost-model/src/cost/filter/constant.rs | 38 - optd-cost-model/src/cost/filter/core.rs | 872 ---- optd-cost-model/src/cost/filter/in_list.rs | 160 - optd-cost-model/src/cost/filter/like.rs | 207 - optd-cost-model/src/cost/filter/log_op.rs | 34 - optd-cost-model/src/cost/filter/mod.rs | 7 - optd-cost-model/src/cost/join/core.rs | 1275 ----- optd-cost-model/src/cost/join/hash_join.rs | 48 - optd-cost-model/src/cost/join/mod.rs | 72 - .../src/cost/join/nested_loop_join.rs | 46 - optd-cost-model/src/cost/limit.rs | 28 - optd-cost-model/src/cost/mod.rs | 4 - optd-cost-model/src/cost_model.rs | 268 - optd-cost-model/src/lib.rs | 191 - optd-cost-model/src/memo_ext.rs | 28 - optd-cost-model/src/stats/arith_encoder.rs | 69 - optd-cost-model/src/stats/mod.rs | 211 - .../src/stats/utilities/counter.rs | 204 - optd-cost-model/src/stats/utilities/mod.rs | 3 - .../src/stats/utilities/simple_map.rs | 21 - .../src/stats/utilities/tdigest.rs | 393 -- optd-cost-model/src/storage/mock.rs | 89 - optd-cost-model/src/storage/mod.rs | 32 - optd-cost-model/src/storage/persistent.rs | 166 - optd-cost-model/src/test_utils.rs | 565 --- optd-cost-model/src/utils.rs | 118 - optd-persistent/Cargo.lock | 2814 ----------- optd-persistent/Cargo.toml | 24 - optd-persistent/README.md | 35 - optd-persistent/src/bin/init.rs | 404 -- optd-persistent/src/bin/migrate.rs | 16 - .../src/cost_model/catalog/mock_catalog.rs | 165 - optd-persistent/src/cost_model/catalog/mod.rs | 1 - optd-persistent/src/cost_model/interface.rs | 177 - optd-persistent/src/cost_model/mod.rs | 4 - optd-persistent/src/cost_model/orm.rs | 1478 ------ optd-persistent/src/db/init.db | Bin 147456 -> 0 bytes optd-persistent/src/entities/attribute.rs | 73 - .../entities/attribute_constraint_junction.rs | 46 - .../attribute_foreign_constraint_junction.rs | 46 - .../src/entities/cascades_group.rs | 83 - .../src/entities/constraint_metadata.rs | 68 - .../src/entities/database_metadata.rs | 26 - optd-persistent/src/entities/event.rs | 43 - optd-persistent/src/entities/group_winner.rs | 76 - .../src/entities/index_metadata.rs | 48 - .../src/entities/logical_children.rs | 46 - .../src/entities/logical_expression.rs | 49 - .../src/entities/logical_property.rs | 33 - optd-persistent/src/entities/mod.rs | 31 - .../src/entities/namespace_metadata.rs | 41 - .../src/entities/physical_children.rs | 46 - .../src/entities/physical_expression.rs | 94 - ...ysical_expression_to_statistic_junction.rs | 46 - .../src/entities/physical_property.rs | 33 - optd-persistent/src/entities/plan_cost.rs | 58 - optd-persistent/src/entities/predicate.rs | 60 - .../src/entities/predicate_children.rs | 34 - .../predicate_logical_expression_junction.rs | 46 - .../predicate_physical_expression_junction.rs | 46 - optd-persistent/src/entities/prelude.rs | 25 - optd-persistent/src/entities/statistic.rs | 86 - .../statistic_to_attribute_junction.rs | 46 - .../src/entities/table_metadata.rs | 65 - optd-persistent/src/entities/trigger.rs | 42 - .../src/entities/versioned_statistic.rs | 47 - optd-persistent/src/lib.rs | 114 - optd-persistent/src/main.rs | 112 - optd-persistent/src/memo/expression.rs | 73 - optd-persistent/src/memo/interface.rs | 141 - optd-persistent/src/memo/mod.rs | 4 - optd-persistent/src/memo/orm.rs | 213 - .../catalog/m20241029_000001_attribute.rs | 51 - ...29_000001_attribute_constraint_junction.rs | 69 - ...1_attribute_foreign_constraint_junction.rs | 73 - .../m20241029_000001_constraint_metadata.rs | 65 - .../m20241029_000001_database_metadata.rs | 35 - .../m20241029_000001_index_metadata.rs | 59 - .../m20241029_000001_namespace_metadata.rs | 45 - .../m20241029_000001_table_metadata.rs | 45 - .../catalog/m20241029_000001_trigger.rs | 54 - optd-persistent/src/migrator/catalog/mod.rs | 19 - .../cost_model/m20241029_000001_event.rs | 40 - ...ysical_expression_to_statistic_junction.rs | 75 - .../cost_model/m20241029_000001_plan_cost.rs | 73 - .../cost_model/m20241029_000001_statistic.rs | 64 - ..._000001_statistic_to_attribute_junction.rs | 70 - .../m20241029_000001_versioned_statistic.rs | 57 - .../src/migrator/cost_model/mod.rs | 13 - .../memo/m20241029_000001_cascades_group.rs | 121 - .../memo/m20241029_000001_group_winner.rs | 103 - .../memo/m20241029_000001_logical_children.rs | 65 - .../m20241029_000001_logical_expression.rs | 82 - .../memo/m20241029_000001_logical_property.rs | 50 - .../m20241029_000001_physical_children.rs | 70 - .../m20241029_000001_physical_expression.rs | 83 - .../m20241029_000001_physical_property.rs | 53 - .../memo/m20241029_000001_predicate.rs | 46 - .../m20241029_000001_predicate_children.rs | 61 - ...1_predicate_logical_expression_junction.rs | 72 - ..._predicate_physical_expression_junction.rs | 74 - optd-persistent/src/migrator/memo/mod.rs | 28 - optd-persistent/src/migrator/mod.rs | 42 - schema/all_tables.dbml | 216 - 132 files changed, 1 insertion(+), 25615 deletions(-) delete mode 100644 .gitignore delete mode 100644 Cargo.lock delete mode 100644 optd-cost-model/Cargo.lock delete mode 100644 optd-cost-model/Cargo.toml delete mode 100644 optd-cost-model/src/common/mod.rs delete mode 100644 optd-cost-model/src/common/nodes.rs delete mode 100644 optd-cost-model/src/common/predicates/attr_index_pred.rs delete mode 100644 optd-cost-model/src/common/predicates/bin_op_pred.rs delete mode 100644 optd-cost-model/src/common/predicates/cast_pred.rs delete mode 100644 optd-cost-model/src/common/predicates/constant_pred.rs delete mode 100644 optd-cost-model/src/common/predicates/data_type_pred.rs delete mode 100644 optd-cost-model/src/common/predicates/func_pred.rs delete mode 100644 optd-cost-model/src/common/predicates/in_list_pred.rs delete mode 100644 optd-cost-model/src/common/predicates/like_pred.rs delete mode 100644 optd-cost-model/src/common/predicates/list_pred.rs delete mode 100644 optd-cost-model/src/common/predicates/log_op_pred.rs delete mode 100644 optd-cost-model/src/common/predicates/mod.rs delete mode 100644 optd-cost-model/src/common/predicates/sort_order_pred.rs delete mode 100644 optd-cost-model/src/common/predicates/un_op_pred.rs delete mode 100644 optd-cost-model/src/common/properties/attr_ref.rs delete mode 100644 optd-cost-model/src/common/properties/mod.rs delete mode 100644 optd-cost-model/src/common/properties/schema.rs delete mode 100644 optd-cost-model/src/common/types.rs delete mode 100644 optd-cost-model/src/common/values.rs delete mode 100644 optd-cost-model/src/cost/agg.rs delete mode 100644 optd-cost-model/src/cost/filter/attribute.rs delete mode 100644 optd-cost-model/src/cost/filter/comp_op.rs delete mode 100644 optd-cost-model/src/cost/filter/constant.rs delete mode 100644 optd-cost-model/src/cost/filter/core.rs delete mode 100644 optd-cost-model/src/cost/filter/in_list.rs delete mode 100644 optd-cost-model/src/cost/filter/like.rs delete mode 100644 optd-cost-model/src/cost/filter/log_op.rs delete mode 100644 optd-cost-model/src/cost/filter/mod.rs delete mode 100644 optd-cost-model/src/cost/join/core.rs delete mode 100644 optd-cost-model/src/cost/join/hash_join.rs delete mode 100644 optd-cost-model/src/cost/join/mod.rs delete mode 100644 optd-cost-model/src/cost/join/nested_loop_join.rs delete mode 100644 optd-cost-model/src/cost/limit.rs delete mode 100644 optd-cost-model/src/cost/mod.rs delete mode 100644 optd-cost-model/src/cost_model.rs delete mode 100644 optd-cost-model/src/lib.rs delete mode 100644 optd-cost-model/src/memo_ext.rs delete mode 100644 optd-cost-model/src/stats/arith_encoder.rs delete mode 100644 optd-cost-model/src/stats/mod.rs delete mode 100644 optd-cost-model/src/stats/utilities/counter.rs delete mode 100644 optd-cost-model/src/stats/utilities/mod.rs delete mode 100644 optd-cost-model/src/stats/utilities/simple_map.rs delete mode 100644 optd-cost-model/src/stats/utilities/tdigest.rs delete mode 100644 optd-cost-model/src/storage/mock.rs delete mode 100644 optd-cost-model/src/storage/mod.rs delete mode 100644 optd-cost-model/src/storage/persistent.rs delete mode 100644 optd-cost-model/src/test_utils.rs delete mode 100644 optd-cost-model/src/utils.rs delete mode 100644 optd-persistent/Cargo.lock delete mode 100644 optd-persistent/Cargo.toml delete mode 100644 optd-persistent/README.md delete mode 100644 optd-persistent/src/bin/init.rs delete mode 100644 optd-persistent/src/bin/migrate.rs delete mode 100644 optd-persistent/src/cost_model/catalog/mock_catalog.rs delete mode 100644 optd-persistent/src/cost_model/catalog/mod.rs delete mode 100644 optd-persistent/src/cost_model/interface.rs delete mode 100644 optd-persistent/src/cost_model/mod.rs delete mode 100644 optd-persistent/src/cost_model/orm.rs delete mode 100644 optd-persistent/src/db/init.db delete mode 100644 optd-persistent/src/entities/attribute.rs delete mode 100644 optd-persistent/src/entities/attribute_constraint_junction.rs delete mode 100644 optd-persistent/src/entities/attribute_foreign_constraint_junction.rs delete mode 100644 optd-persistent/src/entities/cascades_group.rs delete mode 100644 optd-persistent/src/entities/constraint_metadata.rs delete mode 100644 optd-persistent/src/entities/database_metadata.rs delete mode 100644 optd-persistent/src/entities/event.rs delete mode 100644 optd-persistent/src/entities/group_winner.rs delete mode 100644 optd-persistent/src/entities/index_metadata.rs delete mode 100644 optd-persistent/src/entities/logical_children.rs delete mode 100644 optd-persistent/src/entities/logical_expression.rs delete mode 100644 optd-persistent/src/entities/logical_property.rs delete mode 100644 optd-persistent/src/entities/mod.rs delete mode 100644 optd-persistent/src/entities/namespace_metadata.rs delete mode 100644 optd-persistent/src/entities/physical_children.rs delete mode 100644 optd-persistent/src/entities/physical_expression.rs delete mode 100644 optd-persistent/src/entities/physical_expression_to_statistic_junction.rs delete mode 100644 optd-persistent/src/entities/physical_property.rs delete mode 100644 optd-persistent/src/entities/plan_cost.rs delete mode 100644 optd-persistent/src/entities/predicate.rs delete mode 100644 optd-persistent/src/entities/predicate_children.rs delete mode 100644 optd-persistent/src/entities/predicate_logical_expression_junction.rs delete mode 100644 optd-persistent/src/entities/predicate_physical_expression_junction.rs delete mode 100644 optd-persistent/src/entities/prelude.rs delete mode 100644 optd-persistent/src/entities/statistic.rs delete mode 100644 optd-persistent/src/entities/statistic_to_attribute_junction.rs delete mode 100644 optd-persistent/src/entities/table_metadata.rs delete mode 100644 optd-persistent/src/entities/trigger.rs delete mode 100644 optd-persistent/src/entities/versioned_statistic.rs delete mode 100644 optd-persistent/src/lib.rs delete mode 100644 optd-persistent/src/main.rs delete mode 100644 optd-persistent/src/memo/expression.rs delete mode 100644 optd-persistent/src/memo/interface.rs delete mode 100644 optd-persistent/src/memo/mod.rs delete mode 100644 optd-persistent/src/memo/orm.rs delete mode 100644 optd-persistent/src/migrator/catalog/m20241029_000001_attribute.rs delete mode 100644 optd-persistent/src/migrator/catalog/m20241029_000001_attribute_constraint_junction.rs delete mode 100644 optd-persistent/src/migrator/catalog/m20241029_000001_attribute_foreign_constraint_junction.rs delete mode 100644 optd-persistent/src/migrator/catalog/m20241029_000001_constraint_metadata.rs delete mode 100644 optd-persistent/src/migrator/catalog/m20241029_000001_database_metadata.rs delete mode 100644 optd-persistent/src/migrator/catalog/m20241029_000001_index_metadata.rs delete mode 100644 optd-persistent/src/migrator/catalog/m20241029_000001_namespace_metadata.rs delete mode 100644 optd-persistent/src/migrator/catalog/m20241029_000001_table_metadata.rs delete mode 100644 optd-persistent/src/migrator/catalog/m20241029_000001_trigger.rs delete mode 100644 optd-persistent/src/migrator/catalog/mod.rs delete mode 100644 optd-persistent/src/migrator/cost_model/m20241029_000001_event.rs delete mode 100644 optd-persistent/src/migrator/cost_model/m20241029_000001_physical_expression_to_statistic_junction.rs delete mode 100644 optd-persistent/src/migrator/cost_model/m20241029_000001_plan_cost.rs delete mode 100644 optd-persistent/src/migrator/cost_model/m20241029_000001_statistic.rs delete mode 100644 optd-persistent/src/migrator/cost_model/m20241029_000001_statistic_to_attribute_junction.rs delete mode 100644 optd-persistent/src/migrator/cost_model/m20241029_000001_versioned_statistic.rs delete mode 100644 optd-persistent/src/migrator/cost_model/mod.rs delete mode 100644 optd-persistent/src/migrator/memo/m20241029_000001_cascades_group.rs delete mode 100644 optd-persistent/src/migrator/memo/m20241029_000001_group_winner.rs delete mode 100644 optd-persistent/src/migrator/memo/m20241029_000001_logical_children.rs delete mode 100644 optd-persistent/src/migrator/memo/m20241029_000001_logical_expression.rs delete mode 100644 optd-persistent/src/migrator/memo/m20241029_000001_logical_property.rs delete mode 100644 optd-persistent/src/migrator/memo/m20241029_000001_physical_children.rs delete mode 100644 optd-persistent/src/migrator/memo/m20241029_000001_physical_expression.rs delete mode 100644 optd-persistent/src/migrator/memo/m20241029_000001_physical_property.rs delete mode 100644 optd-persistent/src/migrator/memo/m20241029_000001_predicate.rs delete mode 100644 optd-persistent/src/migrator/memo/m20241029_000001_predicate_children.rs delete mode 100644 optd-persistent/src/migrator/memo/m20241029_000001_predicate_logical_expression_junction.rs delete mode 100644 optd-persistent/src/migrator/memo/m20241029_000001_predicate_physical_expression_junction.rs delete mode 100644 optd-persistent/src/migrator/memo/mod.rs delete mode 100644 optd-persistent/src/migrator/mod.rs delete mode 100644 schema/all_tables.dbml diff --git a/.gitignore b/.gitignore deleted file mode 100644 index db6bb3a..0000000 --- a/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -debug/ -target/ - -# These are backup files generated by rustfmt -**/*.rs.bk - -**/*.db -!init.db - -.DS_Store - -optd-persistent/sql/ diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index e81b740..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,4479 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "const-random", - "getrandom", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "aliasable" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" - -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" -dependencies = [ - "alloc-no-stdlib", -] - -[[package]] -name = "allocator-api2" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anstream" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" - -[[package]] -name = "anstyle-parse" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" -dependencies = [ - "anstyle", - "windows-sys 0.59.0", -] - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "arrow" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fab9e93ba8ce88a37d5a30dce4b9913b75413dc1ac56cb5d72e5a840543f829" -dependencies = [ - "ahash 0.8.11", - "arrow-arith", - "arrow-array", - "arrow-buffer", - "arrow-cast", - "arrow-csv", - "arrow-data", - "arrow-ipc", - "arrow-json", - "arrow-ord", - "arrow-row", - "arrow-schema 47.0.0", - "arrow-select", - "arrow-string", -] - -[[package]] -name = "arrow-arith" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc1d4e368e87ad9ee64f28b9577a3834ce10fe2703a26b28417d485bbbdff956" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema 47.0.0", - "chrono", - "half", - "num", -] - -[[package]] -name = "arrow-array" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d02efa7253ede102d45a4e802a129e83bcc3f49884cab795b1ac223918e4318d" -dependencies = [ - "ahash 0.8.11", - "arrow-buffer", - "arrow-data", - "arrow-schema 47.0.0", - "chrono", - "chrono-tz", - "half", - "hashbrown 0.14.5", - "num", -] - -[[package]] -name = "arrow-buffer" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda119225204141138cb0541c692fbfef0e875ba01bfdeaed09e9d354f9d6195" -dependencies = [ - "bytes", - "half", - "num", -] - -[[package]] -name = "arrow-cast" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d825d51b9968868d50bc5af92388754056796dbc62a4e25307d588a1fc84dee" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema 47.0.0", - "arrow-select", - "chrono", - "comfy-table", - "half", - "lexical-core", - "num", -] - -[[package]] -name = "arrow-csv" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43ef855dc6b126dc197f43e061d4de46b9d4c033aa51c2587657f7508242cef1" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-cast", - "arrow-data", - "arrow-schema 47.0.0", - "chrono", - "csv", - "csv-core", - "lazy_static", - "lexical-core", - "regex", -] - -[[package]] -name = "arrow-data" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "475a4c3699c8b4095ca61cecf15da6f67841847a5f5aac983ccb9a377d02f73a" -dependencies = [ - "arrow-buffer", - "arrow-schema 47.0.0", - "half", - "num", -] - -[[package]] -name = "arrow-ipc" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1248005c8ac549f869b7a840859d942bf62471479c1a2d82659d453eebcd166a" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-cast", - "arrow-data", - "arrow-schema 47.0.0", - "flatbuffers", -] - -[[package]] -name = "arrow-json" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03d7e3b04dd688ccec354fe449aed56b831679f03e44ee2c1cfc4045067b69c" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-cast", - "arrow-data", - "arrow-schema 47.0.0", - "chrono", - "half", - "indexmap 2.6.0", - "lexical-core", - "num", - "serde", - "serde_json", -] - -[[package]] -name = "arrow-ord" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b87aa408ea6a6300e49eb2eba0c032c88ed9dc19e0a9948489c55efdca71f4" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema 47.0.0", - "arrow-select", - "half", - "num", -] - -[[package]] -name = "arrow-row" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114a348ab581e7c9b6908fcab23cb39ff9f060eb19e72b13f8fb8eaa37f65d22" -dependencies = [ - "ahash 0.8.11", - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema 47.0.0", - "half", - "hashbrown 0.14.5", -] - -[[package]] -name = "arrow-schema" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1d179c117b158853e0101bfbed5615e86fe97ee356b4af901f1c5001e1ce4b" - -[[package]] -name = "arrow-schema" -version = "53.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539ada65246b949bd99ffa0881a9a15a4a529448af1a07a9838dd78617dafab1" - -[[package]] -name = "arrow-select" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5c71e003202e67e9db139e5278c79f5520bb79922261dfe140e4637ee8b6108" -dependencies = [ - "ahash 0.8.11", - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema 47.0.0", - "num", -] - -[[package]] -name = "arrow-string" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cebbb282d6b9244895f4a9a912e55e57bce112554c7fa91fcec5459cb421ab" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema 47.0.0", - "arrow-select", - "num", - "regex", - "regex-syntax 0.7.5", -] - -[[package]] -name = "assert_approx_eq" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c07dab4369547dbe5114677b33fbbf724971019f3818172d59a97a61c774ffd" - -[[package]] -name = "async-compression" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb8f1d480b0ea3783ab015936d2a55c87e219676f0c0b7dec61494043f21857" -dependencies = [ - "bzip2", - "flate2", - "futures-core", - "futures-io", - "memchr", - "pin-project-lite", - "tokio", - "xz2", - "zstd 0.13.2", - "zstd-safe 7.2.1", -] - -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "async-trait" -version = "0.1.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "atoi" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" -dependencies = [ - "num-traits", -] - -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "backtrace" -version = "0.3.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "base64" -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 = "bigdecimal" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f850665a0385e070b64c38d2354e6c104c8479c59868d1e48a0c13ee2c7a1c1" -dependencies = [ - "autocfg", - "libm", - "num-bigint", - "num-integer", - "num-traits", - "serde", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" -dependencies = [ - "serde", -] - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest", -] - -[[package]] -name = "blake3" -version = "1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", -] - -[[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 = "borsh" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2506947f73ad44e344215ccd6403ac2ae18cd8e046e581a441bf8d199f257f03" -dependencies = [ - "borsh-derive", - "cfg_aliases", -] - -[[package]] -name = "borsh-derive" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2593a3b8b938bd68373196c9832f516be11fa487ef4ae745eb282e6a56a7244" -dependencies = [ - "once_cell", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "brotli" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "2.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "bytecheck" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" -dependencies = [ - "bytecheck_derive", - "ptr_meta", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" - -[[package]] -name = "bzip2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" -dependencies = [ - "bzip2-sys", - "libc", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "cc" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" -dependencies = [ - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-targets 0.52.6", -] - -[[package]] -name = "chrono-tz" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e" -dependencies = [ - "chrono", - "chrono-tz-build", - "phf", -] - -[[package]] -name = "chrono-tz-build" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" -dependencies = [ - "parse-zoneinfo", - "phf", - "phf_codegen", -] - -[[package]] -name = "clap" -version = "4.5.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "clap_lex" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" - -[[package]] -name = "colorchoice" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" - -[[package]] -name = "comfy-table" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f165e7b643266ea80cb858aed492ad9280e3e05ce24d4a99d7d7b889b6a4d9" -dependencies = [ - "strum 0.26.3", - "strum_macros 0.26.4", - "unicode-width", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom", - "once_cell", - "tiny-keccak", -] - -[[package]] -name = "constant_time_eq" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" -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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -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 = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[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 = "csv" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" -dependencies = [ - "memchr", -] - -[[package]] -name = "darling" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.87", -] - -[[package]] -name = "darling_macro" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core", -] - -[[package]] -name = "datafusion" -version = "32.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7014432223f4d721cb9786cd88bb89e7464e0ba984d4a7f49db7787f5f268674" -dependencies = [ - "ahash 0.8.11", - "arrow", - "arrow-array", - "arrow-schema 47.0.0", - "async-compression", - "async-trait", - "bytes", - "bzip2", - "chrono", - "dashmap", - "datafusion-common", - "datafusion-execution", - "datafusion-expr", - "datafusion-optimizer", - "datafusion-physical-expr", - "datafusion-physical-plan", - "datafusion-sql", - "flate2", - "futures", - "glob", - "half", - "hashbrown 0.14.5", - "indexmap 2.6.0", - "itertools 0.11.0", - "log", - "num_cpus", - "object_store", - "parking_lot", - "parquet", - "percent-encoding", - "pin-project-lite", - "rand", - "sqlparser", - "tempfile", - "tokio", - "tokio-util", - "url", - "uuid", - "xz2", - "zstd 0.12.4", -] - -[[package]] -name = "datafusion-common" -version = "32.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3903ed8f102892f17b48efa437f3542159241d41c564f0d1e78efdc5e663aa" -dependencies = [ - "ahash 0.8.11", - "arrow", - "arrow-array", - "arrow-buffer", - "arrow-schema 47.0.0", - "chrono", - "half", - "num_cpus", - "object_store", - "parquet", - "sqlparser", -] - -[[package]] -name = "datafusion-execution" -version = "32.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780b73b2407050e53f51a9781868593f694102c59e622de9a8aafc0343c4f237" -dependencies = [ - "arrow", - "chrono", - "dashmap", - "datafusion-common", - "datafusion-expr", - "futures", - "hashbrown 0.14.5", - "log", - "object_store", - "parking_lot", - "rand", - "tempfile", - "url", -] - -[[package]] -name = "datafusion-expr" -version = "32.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24c382676338d8caba6c027ba0da47260f65ffedab38fda78f6d8043f607557c" -dependencies = [ - "ahash 0.8.11", - "arrow", - "arrow-array", - "datafusion-common", - "sqlparser", - "strum 0.25.0", - "strum_macros 0.25.3", -] - -[[package]] -name = "datafusion-optimizer" -version = "32.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2904a432f795484fd45e29ded4537152adb60f636c05691db34fcd94c92c96" -dependencies = [ - "arrow", - "async-trait", - "chrono", - "datafusion-common", - "datafusion-expr", - "datafusion-physical-expr", - "hashbrown 0.14.5", - "itertools 0.11.0", - "log", - "regex-syntax 0.7.5", -] - -[[package]] -name = "datafusion-physical-expr" -version = "32.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b4968e9a998dc0476c4db7a82f280e2026b25f464e4aa0c3bb9807ee63ddfd" -dependencies = [ - "ahash 0.8.11", - "arrow", - "arrow-array", - "arrow-buffer", - "arrow-schema 47.0.0", - "base64 0.21.7", - "blake2", - "blake3", - "chrono", - "datafusion-common", - "datafusion-expr", - "half", - "hashbrown 0.14.5", - "hex", - "indexmap 2.6.0", - "itertools 0.11.0", - "libc", - "log", - "md-5", - "paste", - "petgraph", - "rand", - "regex", - "sha2", - "unicode-segmentation", - "uuid", -] - -[[package]] -name = "datafusion-physical-plan" -version = "32.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd0d1fe54e37a47a2d58a1232c22786f2c28ad35805fdcd08f0253a8b0aaa90" -dependencies = [ - "ahash 0.8.11", - "arrow", - "arrow-array", - "arrow-buffer", - "arrow-schema 47.0.0", - "async-trait", - "chrono", - "datafusion-common", - "datafusion-execution", - "datafusion-expr", - "datafusion-physical-expr", - "futures", - "half", - "hashbrown 0.14.5", - "indexmap 2.6.0", - "itertools 0.11.0", - "log", - "once_cell", - "parking_lot", - "pin-project-lite", - "rand", - "tokio", - "uuid", -] - -[[package]] -name = "datafusion-sql" -version = "32.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b568d44c87ead99604d704f942e257c8a236ee1bbf890ee3e034ad659dcb2c21" -dependencies = [ - "arrow", - "arrow-schema 47.0.0", - "datafusion-common", - "datafusion-expr", - "log", - "sqlparser", -] - -[[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]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", - "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 = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - -[[package]] -name = "either" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" -dependencies = [ - "serde", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "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 = "5.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "fastrand" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flatbuffers" -version = "23.5.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dac53e22462d78c16d64a1cd22371b54cc3fe94aa15e7886a2fa6e5d1ab8640" -dependencies = [ - "bitflags 1.3.2", - "rustc_version", -] - -[[package]] -name = "flate2" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "flume" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" -dependencies = [ - "futures-core", - "futures-sink", - "spin", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "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.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "half" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" -dependencies = [ - "cfg-if", - "crunchy", - "num-traits", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash 0.8.11", - "allocator-api2", -] - -[[package]] -name = "hashbrown" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" - -[[package]] -name = "hashlink" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" -dependencies = [ - "hashbrown 0.14.5", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hex" -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 = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "iana-time-zone" -version = "0.1.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" -dependencies = [ - "equivalent", - "hashbrown 0.15.1", - "serde", -] - -[[package]] -name = "inherent" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0122b7114117e64a63ac49f752a5ca4624d534c7b1c7de796ac196381cd2d947" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "integer-encoding" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - -[[package]] -name = "jobserver" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] - -[[package]] -name = "lexical-core" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" -dependencies = [ - "lexical-parse-float", - "lexical-parse-integer", - "lexical-util", - "lexical-write-float", - "lexical-write-integer", -] - -[[package]] -name = "lexical-parse-float" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" -dependencies = [ - "lexical-parse-integer", - "lexical-util", - "static_assertions", -] - -[[package]] -name = "lexical-parse-integer" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" -dependencies = [ - "lexical-util", - "static_assertions", -] - -[[package]] -name = "lexical-util" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" -dependencies = [ - "static_assertions", -] - -[[package]] -name = "lexical-write-float" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" -dependencies = [ - "lexical-util", - "lexical-write-integer", - "static_assertions", -] - -[[package]] -name = "lexical-write-integer" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" -dependencies = [ - "lexical-util", - "static_assertions", -] - -[[package]] -name = "libc" -version = "0.2.162" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" - -[[package]] -name = "libm" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" - -[[package]] -name = "libsqlite3-sys" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[package]] -name = "litemap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "lz4" -version = "1.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d1febb2b4a79ddd1980eede06a8f7902197960aa0383ffcfdd62fe723036725" -dependencies = [ - "lz4-sys", -] - -[[package]] -name = "lz4-sys" -version = "1.11.1+lz4-1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bd8c0d6c6ed0cd30b3652886bb8711dc4bb01d637a68105a3d5158039b418e6" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "lzma-sys" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[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.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" -dependencies = [ - "adler2", -] - -[[package]] -name = "mio" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" -dependencies = [ - "hermit-abi", - "libc", - "wasi", - "windows-sys 0.52.0", -] - -[[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 = "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-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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "object" -version = "0.36.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" -dependencies = [ - "memchr", -] - -[[package]] -name = "object_store" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f930c88a43b1c3f6e776dfe495b4afab89882dbc81530c632db2ed65451ebcb4" -dependencies = [ - "async-trait", - "bytes", - "chrono", - "futures", - "humantime", - "itertools 0.11.0", - "parking_lot", - "percent-encoding", - "snafu", - "tokio", - "tracing", - "url", - "walkdir", -] - -[[package]] -name = "once_cell" -version = "1.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" - -[[package]] -name = "optd-cost-model" -version = "0.1.0" -dependencies = [ - "arrow-schema 53.2.0", - "assert_approx_eq", - "async-trait", - "chrono", - "crossbeam", - "datafusion", - "datafusion-expr", - "itertools 0.13.0", - "optd-persistent", - "ordered-float 4.5.0", - "rand", - "serde", - "serde_json", - "serde_with", - "test-case", - "tokio", - "trait-variant", -] - -[[package]] -name = "optd-persistent" -version = "0.1.0" -dependencies = [ - "async-stream", - "async-trait", - "num_enum", - "sea-orm", - "sea-orm-migration", - "serde_json", - "strum 0.26.3", - "tokio", - "trait-variant", -] - -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "ordered-float" -version = "3.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" -dependencies = [ - "num-traits", -] - -[[package]] -name = "ordered-float" -version = "4.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c65ee1f9701bf938026630b455d5315f490640234259037edb259798b3bcf85e" -dependencies = [ - "num-traits", -] - -[[package]] -name = "ouroboros" -version = "0.18.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "944fa20996a25aded6b4795c6d63f10014a7a83f8be9828a11860b08c5fc4a67" -dependencies = [ - "aliasable", - "ouroboros_macro", - "static_assertions", -] - -[[package]] -name = "ouroboros_macro" -version = "0.18.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39b0deead1528fd0e5947a8546a9642a9777c25f6e1e26f34c97b204bbb465bd" -dependencies = [ - "heck 0.4.1", - "itertools 0.12.1", - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.6", -] - -[[package]] -name = "parquet" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0463cc3b256d5f50408c49a4be3a16674f4c8ceef60941709620a062b1f6bf4d" -dependencies = [ - "ahash 0.8.11", - "arrow-array", - "arrow-buffer", - "arrow-cast", - "arrow-data", - "arrow-ipc", - "arrow-schema 47.0.0", - "arrow-select", - "base64 0.21.7", - "brotli", - "bytes", - "chrono", - "flate2", - "futures", - "hashbrown 0.14.5", - "lz4", - "num", - "num-bigint", - "object_store", - "paste", - "seq-macro", - "snap", - "thrift", - "tokio", - "twox-hash", - "zstd 0.12.4", -] - -[[package]] -name = "parse-zoneinfo" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" -dependencies = [ - "regex", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset", - "indexmap 2.6.0", -] - -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_codegen" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared", - "rand", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" - -[[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 = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "proc-macro-crate" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" -dependencies = [ - "toml_edit", -] - -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "proc-macro2" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "proc-macro2-diagnostics" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", - "version_check", - "yansi", -] - -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "redox_syscall" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" -dependencies = [ - "bitflags 2.6.0", -] - -[[package]] -name = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - -[[package]] -name = "rend" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" -dependencies = [ - "bytecheck", -] - -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom", - "libc", - "spin", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rkyv" -version = "0.7.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" -dependencies = [ - "bitvec", - "bytecheck", - "bytes", - "hashbrown 0.12.3", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", - "uuid", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[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 = "rust_decimal" -version = "1.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" -dependencies = [ - "arrayvec", - "borsh", - "bytes", - "num-traits", - "rand", - "rkyv", - "serde", - "serde_json", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" -dependencies = [ - "bitflags 2.6.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.23.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" -dependencies = [ - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" - -[[package]] -name = "rustls-webpki" -version = "0.102.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" - -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sea-bae" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f694a6ab48f14bc063cfadff30ab551d3c7e46d8f81836c51989d548f44a2a25" -dependencies = [ - "heck 0.4.1", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "sea-orm" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5680a8b686985116607ef5f5af2b1f9e1cc2c228330e93101816a0baa279afa" -dependencies = [ - "async-stream", - "async-trait", - "bigdecimal", - "chrono", - "futures", - "log", - "ouroboros", - "rust_decimal", - "sea-orm-macros", - "sea-query", - "sea-query-binder", - "serde", - "serde_json", - "sqlx", - "strum 0.26.3", - "thiserror", - "time", - "tracing", - "url", - "uuid", -] - -[[package]] -name = "sea-orm-cli" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a157f42d291ccbd6e913b9d9b12dbe2ccbcf0472efc60c8715dd1254083aec" -dependencies = [ - "chrono", - "clap", - "dotenvy", - "glob", - "regex", - "sea-schema", - "tracing", - "tracing-subscriber", - "url", -] - -[[package]] -name = "sea-orm-macros" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a239e3bb1b566ad4ec2654d0d193d6ceddfd733487edc9c21a64d214c773910" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "sea-bae", - "syn 2.0.87", - "unicode-ident", -] - -[[package]] -name = "sea-orm-migration" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63ba07e9f2479cc671758fcb1edee42ff2e32c34b3e67ab41d0af1e41f73c74e" -dependencies = [ - "async-trait", - "clap", - "dotenvy", - "futures", - "sea-orm", - "sea-orm-cli", - "sea-schema", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "sea-query" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff504d13b5e4b52fffcf2fb203d0352a5722fa5151696db768933e41e1e591bb" -dependencies = [ - "bigdecimal", - "chrono", - "inherent", - "ordered-float 3.9.2", - "rust_decimal", - "sea-query-derive", - "serde_json", - "time", - "uuid", -] - -[[package]] -name = "sea-query-binder" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0019f47430f7995af63deda77e238c17323359af241233ec768aba1faea7608" -dependencies = [ - "bigdecimal", - "chrono", - "rust_decimal", - "sea-query", - "serde_json", - "sqlx", - "time", - "uuid", -] - -[[package]] -name = "sea-query-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9834af2c4bd8c5162f00c89f1701fb6886119a88062cf76fe842ea9e232b9839" -dependencies = [ - "darling", - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 2.0.87", - "thiserror", -] - -[[package]] -name = "sea-schema" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aab1592d17860a9a8584d9b549aebcd06f7bdc3ff615f71752486ba0b05b1e6e" -dependencies = [ - "futures", - "sea-query", - "sea-schema-derive", -] - -[[package]] -name = "sea-schema-derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "debdc8729c37fdbf88472f97fd470393089f997a909e535ff67c544d18cfccf0" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" - -[[package]] -name = "seq-macro" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" - -[[package]] -name = "serde" -version = "1.0.215" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.215" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "serde_json" -version = "1.0.132" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" -dependencies = [ - "base64 0.22.1", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.6.0", - "serde", - "serde_derive", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[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 = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -dependencies = [ - "serde", -] - -[[package]] -name = "snafu" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" -dependencies = [ - "doc-comment", - "snafu-derive", -] - -[[package]] -name = "snafu-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "snap" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" - -[[package]] -name = "socket2" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -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.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" -dependencies = [ - "nom", - "unicode_categories", -] - -[[package]] -name = "sqlparser" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0272b7bb0a225320170c99901b4b5fb3a4384e255a7f2cc228f61e2ba3893e75" -dependencies = [ - "log", - "sqlparser_derive", -] - -[[package]] -name = "sqlparser_derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55fe75cb4a364c7f7ae06c7dbbc8d84bddd85d6cdf9975963c3935bc1991761e" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "sqlx" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93334716a037193fac19df402f8571269c84a00852f6a7066b5d2616dcd64d3e" -dependencies = [ - "sqlx-core", - "sqlx-macros", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", -] - -[[package]] -name = "sqlx-core" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d8060b456358185f7d50c55d9b5066ad956956fddec42ee2e8567134a8936e" -dependencies = [ - "atoi", - "bigdecimal", - "byteorder", - "bytes", - "chrono", - "crc", - "crossbeam-queue", - "either", - "event-listener", - "futures-channel", - "futures-core", - "futures-intrusive", - "futures-io", - "futures-util", - "hashbrown 0.14.5", - "hashlink", - "hex", - "indexmap 2.6.0", - "log", - "memchr", - "once_cell", - "paste", - "percent-encoding", - "rust_decimal", - "rustls", - "rustls-pemfile", - "serde", - "serde_json", - "sha2", - "smallvec", - "sqlformat", - "thiserror", - "time", - "tokio", - "tokio-stream", - "tracing", - "url", - "uuid", - "webpki-roots", -] - -[[package]] -name = "sqlx-macros" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657" -dependencies = [ - "proc-macro2", - "quote", - "sqlx-core", - "sqlx-macros-core", - "syn 2.0.87", -] - -[[package]] -name = "sqlx-macros-core" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1804e8a7c7865599c9c79be146dc8a9fd8cc86935fa641d3ea58e5f0688abaa5" -dependencies = [ - "dotenvy", - "either", - "heck 0.5.0", - "hex", - "once_cell", - "proc-macro2", - "quote", - "serde", - "serde_json", - "sha2", - "sqlx-core", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", - "syn 2.0.87", - "tempfile", - "tokio", - "url", -] - -[[package]] -name = "sqlx-mysql" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" -dependencies = [ - "atoi", - "base64 0.22.1", - "bigdecimal", - "bitflags 2.6.0", - "byteorder", - "bytes", - "chrono", - "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", - "rust_decimal", - "serde", - "sha1", - "sha2", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror", - "time", - "tracing", - "uuid", - "whoami", -] - -[[package]] -name = "sqlx-postgres" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" -dependencies = [ - "atoi", - "base64 0.22.1", - "bigdecimal", - "bitflags 2.6.0", - "byteorder", - "chrono", - "crc", - "dotenvy", - "etcetera", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "hex", - "hkdf", - "hmac", - "home", - "itoa", - "log", - "md-5", - "memchr", - "num-bigint", - "once_cell", - "rand", - "rust_decimal", - "serde", - "serde_json", - "sha2", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror", - "time", - "tracing", - "uuid", - "whoami", -] - -[[package]] -name = "sqlx-sqlite" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680" -dependencies = [ - "atoi", - "chrono", - "flume", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "libsqlite3-sys", - "log", - "percent-encoding", - "serde", - "serde_urlencoded", - "sqlx-core", - "time", - "tracing", - "url", - "uuid", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[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" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" -dependencies = [ - "strum_macros 0.25.3", -] - -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" - -[[package]] -name = "strum_macros" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.87", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.87", -] - -[[package]] -name = "subtle" -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.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" -dependencies = [ - "cfg-if", - "fastrand", - "once_cell", - "rustix", - "windows-sys 0.59.0", -] - -[[package]] -name = "test-case" -version = "3.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8" -dependencies = [ - "test-case-macros", -] - -[[package]] -name = "test-case-core" -version = "3.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "test-case-macros" -version = "3.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", - "test-case-core", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "thrift" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e54bc85fc7faa8bc175c4bab5b92ba8d9a3ce893d0e9f42cc455c8ab16a9e09" -dependencies = [ - "byteorder", - "integer-encoding", - "ordered-float 2.10.1", -] - -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.41.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "socket2", - "tokio-macros", - "windows-sys 0.52.0", -] - -[[package]] -name = "tokio-macros" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "tokio-stream" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" - -[[package]] -name = "toml_edit" -version = "0.22.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" -dependencies = [ - "indexmap 2.6.0", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "matchers", - "once_cell", - "regex", - "sharded-slab", - "thread_local", - "tracing", - "tracing-core", -] - -[[package]] -name = "trait-variant" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "twox-hash" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" -dependencies = [ - "cfg-if", - "static_assertions", -] - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - -[[package]] -name = "unicode-ident" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-properties" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode-width" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" - -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "uuid" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" -dependencies = [ - "getrandom", - "serde", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -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.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.87", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" - -[[package]] -name = "webpki-roots" -version = "0.26.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "whoami" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" -dependencies = [ - "redox_syscall", - "wasite", -] - -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" -dependencies = [ - "memchr", -] - -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "xz2" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" -dependencies = [ - "lzma-sys", -] - -[[package]] -name = "yansi" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" - -[[package]] -name = "yoke" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[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.87", -] - -[[package]] -name = "zerofrom" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" - -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "zstd" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" -dependencies = [ - "zstd-safe 6.0.6", -] - -[[package]] -name = "zstd" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" -dependencies = [ - "zstd-safe 7.2.1", -] - -[[package]] -name = "zstd-safe" -version = "6.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-safe" -version = "7.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/Cargo.toml b/Cargo.toml index 540b2a8..14d391c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["optd-cost-model", "optd-persistent"] +members = ["optd-mvp"] resolver = "2" diff --git a/optd-cost-model/Cargo.lock b/optd-cost-model/Cargo.lock deleted file mode 100644 index bf0b367..0000000 --- a/optd-cost-model/Cargo.lock +++ /dev/null @@ -1,4413 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "const-random", - "getrandom", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "aliasable" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" - -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" -dependencies = [ - "alloc-no-stdlib", -] - -[[package]] -name = "allocator-api2" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anstream" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" - -[[package]] -name = "anstyle-parse" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" -dependencies = [ - "anstyle", - "windows-sys 0.59.0", -] - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "arrow" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fab9e93ba8ce88a37d5a30dce4b9913b75413dc1ac56cb5d72e5a840543f829" -dependencies = [ - "ahash 0.8.11", - "arrow-arith", - "arrow-array", - "arrow-buffer", - "arrow-cast", - "arrow-csv", - "arrow-data", - "arrow-ipc", - "arrow-json", - "arrow-ord", - "arrow-row", - "arrow-schema 47.0.0", - "arrow-select", - "arrow-string", -] - -[[package]] -name = "arrow-arith" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc1d4e368e87ad9ee64f28b9577a3834ce10fe2703a26b28417d485bbbdff956" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema 47.0.0", - "chrono", - "half", - "num", -] - -[[package]] -name = "arrow-array" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d02efa7253ede102d45a4e802a129e83bcc3f49884cab795b1ac223918e4318d" -dependencies = [ - "ahash 0.8.11", - "arrow-buffer", - "arrow-data", - "arrow-schema 47.0.0", - "chrono", - "chrono-tz", - "half", - "hashbrown 0.14.5", - "num", -] - -[[package]] -name = "arrow-buffer" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda119225204141138cb0541c692fbfef0e875ba01bfdeaed09e9d354f9d6195" -dependencies = [ - "bytes", - "half", - "num", -] - -[[package]] -name = "arrow-cast" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d825d51b9968868d50bc5af92388754056796dbc62a4e25307d588a1fc84dee" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema 47.0.0", - "arrow-select", - "chrono", - "comfy-table", - "half", - "lexical-core", - "num", -] - -[[package]] -name = "arrow-csv" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43ef855dc6b126dc197f43e061d4de46b9d4c033aa51c2587657f7508242cef1" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-cast", - "arrow-data", - "arrow-schema 47.0.0", - "chrono", - "csv", - "csv-core", - "lazy_static", - "lexical-core", - "regex", -] - -[[package]] -name = "arrow-data" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "475a4c3699c8b4095ca61cecf15da6f67841847a5f5aac983ccb9a377d02f73a" -dependencies = [ - "arrow-buffer", - "arrow-schema 47.0.0", - "half", - "num", -] - -[[package]] -name = "arrow-ipc" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1248005c8ac549f869b7a840859d942bf62471479c1a2d82659d453eebcd166a" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-cast", - "arrow-data", - "arrow-schema 47.0.0", - "flatbuffers", -] - -[[package]] -name = "arrow-json" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03d7e3b04dd688ccec354fe449aed56b831679f03e44ee2c1cfc4045067b69c" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-cast", - "arrow-data", - "arrow-schema 47.0.0", - "chrono", - "half", - "indexmap 2.6.0", - "lexical-core", - "num", - "serde", - "serde_json", -] - -[[package]] -name = "arrow-ord" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b87aa408ea6a6300e49eb2eba0c032c88ed9dc19e0a9948489c55efdca71f4" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema 47.0.0", - "arrow-select", - "half", - "num", -] - -[[package]] -name = "arrow-row" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114a348ab581e7c9b6908fcab23cb39ff9f060eb19e72b13f8fb8eaa37f65d22" -dependencies = [ - "ahash 0.8.11", - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema 47.0.0", - "half", - "hashbrown 0.14.5", -] - -[[package]] -name = "arrow-schema" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1d179c117b158853e0101bfbed5615e86fe97ee356b4af901f1c5001e1ce4b" - -[[package]] -name = "arrow-schema" -version = "53.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539ada65246b949bd99ffa0881a9a15a4a529448af1a07a9838dd78617dafab1" - -[[package]] -name = "arrow-select" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5c71e003202e67e9db139e5278c79f5520bb79922261dfe140e4637ee8b6108" -dependencies = [ - "ahash 0.8.11", - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema 47.0.0", - "num", -] - -[[package]] -name = "arrow-string" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cebbb282d6b9244895f4a9a912e55e57bce112554c7fa91fcec5459cb421ab" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema 47.0.0", - "arrow-select", - "num", - "regex", - "regex-syntax 0.7.5", -] - -[[package]] -name = "async-compression" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb8f1d480b0ea3783ab015936d2a55c87e219676f0c0b7dec61494043f21857" -dependencies = [ - "bzip2", - "flate2", - "futures-core", - "futures-io", - "memchr", - "pin-project-lite", - "tokio", - "xz2", - "zstd 0.13.2", - "zstd-safe 7.2.1", -] - -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "async-trait" -version = "0.1.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "atoi" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" -dependencies = [ - "num-traits", -] - -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "backtrace" -version = "0.3.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "base64" -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 = "bigdecimal" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f850665a0385e070b64c38d2354e6c104c8479c59868d1e48a0c13ee2c7a1c1" -dependencies = [ - "autocfg", - "libm", - "num-bigint", - "num-integer", - "num-traits", - "serde", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" -dependencies = [ - "serde", -] - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest", -] - -[[package]] -name = "blake3" -version = "1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", -] - -[[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 = "borsh" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2506947f73ad44e344215ccd6403ac2ae18cd8e046e581a441bf8d199f257f03" -dependencies = [ - "borsh-derive", - "cfg_aliases", -] - -[[package]] -name = "borsh-derive" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2593a3b8b938bd68373196c9832f516be11fa487ef4ae745eb282e6a56a7244" -dependencies = [ - "once_cell", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "brotli" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "2.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "bytecheck" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" -dependencies = [ - "bytecheck_derive", - "ptr_meta", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" - -[[package]] -name = "bzip2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" -dependencies = [ - "bzip2-sys", - "libc", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "cc" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" -dependencies = [ - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-targets 0.52.6", -] - -[[package]] -name = "chrono-tz" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e" -dependencies = [ - "chrono", - "chrono-tz-build", - "phf", -] - -[[package]] -name = "chrono-tz-build" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" -dependencies = [ - "parse-zoneinfo", - "phf", - "phf_codegen", -] - -[[package]] -name = "clap" -version = "4.5.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "clap_lex" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" - -[[package]] -name = "colorchoice" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" - -[[package]] -name = "comfy-table" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f165e7b643266ea80cb858aed492ad9280e3e05ce24d4a99d7d7b889b6a4d9" -dependencies = [ - "strum 0.26.3", - "strum_macros 0.26.4", - "unicode-width", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom", - "once_cell", - "tiny-keccak", -] - -[[package]] -name = "constant_time_eq" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" -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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -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 = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[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 = "csv" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" -dependencies = [ - "memchr", -] - -[[package]] -name = "darling" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.87", -] - -[[package]] -name = "darling_macro" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core", -] - -[[package]] -name = "datafusion" -version = "32.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7014432223f4d721cb9786cd88bb89e7464e0ba984d4a7f49db7787f5f268674" -dependencies = [ - "ahash 0.8.11", - "arrow", - "arrow-array", - "arrow-schema 47.0.0", - "async-compression", - "async-trait", - "bytes", - "bzip2", - "chrono", - "dashmap", - "datafusion-common", - "datafusion-execution", - "datafusion-expr", - "datafusion-optimizer", - "datafusion-physical-expr", - "datafusion-physical-plan", - "datafusion-sql", - "flate2", - "futures", - "glob", - "half", - "hashbrown 0.14.5", - "indexmap 2.6.0", - "itertools 0.11.0", - "log", - "num_cpus", - "object_store", - "parking_lot", - "parquet", - "percent-encoding", - "pin-project-lite", - "rand", - "sqlparser", - "tempfile", - "tokio", - "tokio-util", - "url", - "uuid", - "xz2", - "zstd 0.12.4", -] - -[[package]] -name = "datafusion-common" -version = "32.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3903ed8f102892f17b48efa437f3542159241d41c564f0d1e78efdc5e663aa" -dependencies = [ - "ahash 0.8.11", - "arrow", - "arrow-array", - "arrow-buffer", - "arrow-schema 47.0.0", - "chrono", - "half", - "num_cpus", - "object_store", - "parquet", - "sqlparser", -] - -[[package]] -name = "datafusion-execution" -version = "32.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780b73b2407050e53f51a9781868593f694102c59e622de9a8aafc0343c4f237" -dependencies = [ - "arrow", - "chrono", - "dashmap", - "datafusion-common", - "datafusion-expr", - "futures", - "hashbrown 0.14.5", - "log", - "object_store", - "parking_lot", - "rand", - "tempfile", - "url", -] - -[[package]] -name = "datafusion-expr" -version = "32.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24c382676338d8caba6c027ba0da47260f65ffedab38fda78f6d8043f607557c" -dependencies = [ - "ahash 0.8.11", - "arrow", - "arrow-array", - "datafusion-common", - "sqlparser", - "strum 0.25.0", - "strum_macros 0.25.3", -] - -[[package]] -name = "datafusion-optimizer" -version = "32.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2904a432f795484fd45e29ded4537152adb60f636c05691db34fcd94c92c96" -dependencies = [ - "arrow", - "async-trait", - "chrono", - "datafusion-common", - "datafusion-expr", - "datafusion-physical-expr", - "hashbrown 0.14.5", - "itertools 0.11.0", - "log", - "regex-syntax 0.7.5", -] - -[[package]] -name = "datafusion-physical-expr" -version = "32.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b4968e9a998dc0476c4db7a82f280e2026b25f464e4aa0c3bb9807ee63ddfd" -dependencies = [ - "ahash 0.8.11", - "arrow", - "arrow-array", - "arrow-buffer", - "arrow-schema 47.0.0", - "base64 0.21.7", - "blake2", - "blake3", - "chrono", - "datafusion-common", - "datafusion-expr", - "half", - "hashbrown 0.14.5", - "hex", - "indexmap 2.6.0", - "itertools 0.11.0", - "libc", - "log", - "md-5", - "paste", - "petgraph", - "rand", - "regex", - "sha2", - "unicode-segmentation", - "uuid", -] - -[[package]] -name = "datafusion-physical-plan" -version = "32.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd0d1fe54e37a47a2d58a1232c22786f2c28ad35805fdcd08f0253a8b0aaa90" -dependencies = [ - "ahash 0.8.11", - "arrow", - "arrow-array", - "arrow-buffer", - "arrow-schema 47.0.0", - "async-trait", - "chrono", - "datafusion-common", - "datafusion-execution", - "datafusion-expr", - "datafusion-physical-expr", - "futures", - "half", - "hashbrown 0.14.5", - "indexmap 2.6.0", - "itertools 0.11.0", - "log", - "once_cell", - "parking_lot", - "pin-project-lite", - "rand", - "tokio", - "uuid", -] - -[[package]] -name = "datafusion-sql" -version = "32.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b568d44c87ead99604d704f942e257c8a236ee1bbf890ee3e034ad659dcb2c21" -dependencies = [ - "arrow", - "arrow-schema 47.0.0", - "datafusion-common", - "datafusion-expr", - "log", - "sqlparser", -] - -[[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]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", - "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 = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - -[[package]] -name = "either" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" -dependencies = [ - "serde", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "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 = "5.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "fastrand" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flatbuffers" -version = "23.5.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dac53e22462d78c16d64a1cd22371b54cc3fe94aa15e7886a2fa6e5d1ab8640" -dependencies = [ - "bitflags 1.3.2", - "rustc_version", -] - -[[package]] -name = "flate2" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "flume" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" -dependencies = [ - "futures-core", - "futures-sink", - "spin", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "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.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "half" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" -dependencies = [ - "cfg-if", - "crunchy", - "num-traits", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash 0.8.11", - "allocator-api2", -] - -[[package]] -name = "hashbrown" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" - -[[package]] -name = "hashlink" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" -dependencies = [ - "hashbrown 0.14.5", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hex" -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 = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "iana-time-zone" -version = "0.1.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" -dependencies = [ - "equivalent", - "hashbrown 0.15.1", - "serde", -] - -[[package]] -name = "inherent" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0122b7114117e64a63ac49f752a5ca4624d534c7b1c7de796ac196381cd2d947" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "integer-encoding" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - -[[package]] -name = "jobserver" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] - -[[package]] -name = "lexical-core" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" -dependencies = [ - "lexical-parse-float", - "lexical-parse-integer", - "lexical-util", - "lexical-write-float", - "lexical-write-integer", -] - -[[package]] -name = "lexical-parse-float" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" -dependencies = [ - "lexical-parse-integer", - "lexical-util", - "static_assertions", -] - -[[package]] -name = "lexical-parse-integer" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" -dependencies = [ - "lexical-util", - "static_assertions", -] - -[[package]] -name = "lexical-util" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" -dependencies = [ - "static_assertions", -] - -[[package]] -name = "lexical-write-float" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" -dependencies = [ - "lexical-util", - "lexical-write-integer", - "static_assertions", -] - -[[package]] -name = "lexical-write-integer" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" -dependencies = [ - "lexical-util", - "static_assertions", -] - -[[package]] -name = "libc" -version = "0.2.162" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" - -[[package]] -name = "libm" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" - -[[package]] -name = "libsqlite3-sys" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[package]] -name = "litemap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "lz4" -version = "1.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d1febb2b4a79ddd1980eede06a8f7902197960aa0383ffcfdd62fe723036725" -dependencies = [ - "lz4-sys", -] - -[[package]] -name = "lz4-sys" -version = "1.11.1+lz4-1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bd8c0d6c6ed0cd30b3652886bb8711dc4bb01d637a68105a3d5158039b418e6" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "lzma-sys" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[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.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" -dependencies = [ - "adler2", -] - -[[package]] -name = "mio" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" -dependencies = [ - "hermit-abi", - "libc", - "wasi", - "windows-sys 0.52.0", -] - -[[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 = "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-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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "object" -version = "0.36.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" -dependencies = [ - "memchr", -] - -[[package]] -name = "object_store" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f930c88a43b1c3f6e776dfe495b4afab89882dbc81530c632db2ed65451ebcb4" -dependencies = [ - "async-trait", - "bytes", - "chrono", - "futures", - "humantime", - "itertools 0.11.0", - "parking_lot", - "percent-encoding", - "snafu", - "tokio", - "tracing", - "url", - "walkdir", -] - -[[package]] -name = "once_cell" -version = "1.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" - -[[package]] -name = "optd-cost-model" -version = "0.1.0" -dependencies = [ - "arrow-schema 53.2.0", - "chrono", - "crossbeam", - "datafusion", - "datafusion-expr", - "itertools 0.13.0", - "optd-persistent", - "ordered-float 4.5.0", - "rand", - "serde", - "serde_json", - "serde_with", -] - -[[package]] -name = "optd-persistent" -version = "0.1.0" -dependencies = [ - "async-stream", - "async-trait", - "sea-orm", - "sea-orm-migration", - "serde_json", - "strum 0.26.3", - "tokio", - "trait-variant", -] - -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "ordered-float" -version = "3.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" -dependencies = [ - "num-traits", -] - -[[package]] -name = "ordered-float" -version = "4.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c65ee1f9701bf938026630b455d5315f490640234259037edb259798b3bcf85e" -dependencies = [ - "num-traits", -] - -[[package]] -name = "ouroboros" -version = "0.18.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "944fa20996a25aded6b4795c6d63f10014a7a83f8be9828a11860b08c5fc4a67" -dependencies = [ - "aliasable", - "ouroboros_macro", - "static_assertions", -] - -[[package]] -name = "ouroboros_macro" -version = "0.18.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39b0deead1528fd0e5947a8546a9642a9777c25f6e1e26f34c97b204bbb465bd" -dependencies = [ - "heck 0.4.1", - "itertools 0.12.1", - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.6", -] - -[[package]] -name = "parquet" -version = "47.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0463cc3b256d5f50408c49a4be3a16674f4c8ceef60941709620a062b1f6bf4d" -dependencies = [ - "ahash 0.8.11", - "arrow-array", - "arrow-buffer", - "arrow-cast", - "arrow-data", - "arrow-ipc", - "arrow-schema 47.0.0", - "arrow-select", - "base64 0.21.7", - "brotli", - "bytes", - "chrono", - "flate2", - "futures", - "hashbrown 0.14.5", - "lz4", - "num", - "num-bigint", - "object_store", - "paste", - "seq-macro", - "snap", - "thrift", - "tokio", - "twox-hash", - "zstd 0.12.4", -] - -[[package]] -name = "parse-zoneinfo" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" -dependencies = [ - "regex", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset", - "indexmap 2.6.0", -] - -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_codegen" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared", - "rand", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" - -[[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 = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "proc-macro-crate" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" -dependencies = [ - "toml_edit", -] - -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "proc-macro2" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "proc-macro2-diagnostics" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", - "version_check", - "yansi", -] - -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "redox_syscall" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" -dependencies = [ - "bitflags 2.6.0", -] - -[[package]] -name = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - -[[package]] -name = "rend" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" -dependencies = [ - "bytecheck", -] - -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom", - "libc", - "spin", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rkyv" -version = "0.7.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" -dependencies = [ - "bitvec", - "bytecheck", - "bytes", - "hashbrown 0.12.3", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", - "uuid", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[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 = "rust_decimal" -version = "1.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" -dependencies = [ - "arrayvec", - "borsh", - "bytes", - "num-traits", - "rand", - "rkyv", - "serde", - "serde_json", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" -dependencies = [ - "bitflags 2.6.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.23.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" -dependencies = [ - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" - -[[package]] -name = "rustls-webpki" -version = "0.102.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" - -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sea-bae" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f694a6ab48f14bc063cfadff30ab551d3c7e46d8f81836c51989d548f44a2a25" -dependencies = [ - "heck 0.4.1", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "sea-orm" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5680a8b686985116607ef5f5af2b1f9e1cc2c228330e93101816a0baa279afa" -dependencies = [ - "async-stream", - "async-trait", - "bigdecimal", - "chrono", - "futures", - "log", - "ouroboros", - "rust_decimal", - "sea-orm-macros", - "sea-query", - "sea-query-binder", - "serde", - "serde_json", - "sqlx", - "strum 0.26.3", - "thiserror", - "time", - "tracing", - "url", - "uuid", -] - -[[package]] -name = "sea-orm-cli" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a157f42d291ccbd6e913b9d9b12dbe2ccbcf0472efc60c8715dd1254083aec" -dependencies = [ - "chrono", - "clap", - "dotenvy", - "glob", - "regex", - "sea-schema", - "tracing", - "tracing-subscriber", - "url", -] - -[[package]] -name = "sea-orm-macros" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a239e3bb1b566ad4ec2654d0d193d6ceddfd733487edc9c21a64d214c773910" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "sea-bae", - "syn 2.0.87", - "unicode-ident", -] - -[[package]] -name = "sea-orm-migration" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63ba07e9f2479cc671758fcb1edee42ff2e32c34b3e67ab41d0af1e41f73c74e" -dependencies = [ - "async-trait", - "clap", - "dotenvy", - "futures", - "sea-orm", - "sea-orm-cli", - "sea-schema", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "sea-query" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff504d13b5e4b52fffcf2fb203d0352a5722fa5151696db768933e41e1e591bb" -dependencies = [ - "bigdecimal", - "chrono", - "inherent", - "ordered-float 3.9.2", - "rust_decimal", - "sea-query-derive", - "serde_json", - "time", - "uuid", -] - -[[package]] -name = "sea-query-binder" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0019f47430f7995af63deda77e238c17323359af241233ec768aba1faea7608" -dependencies = [ - "bigdecimal", - "chrono", - "rust_decimal", - "sea-query", - "serde_json", - "sqlx", - "time", - "uuid", -] - -[[package]] -name = "sea-query-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9834af2c4bd8c5162f00c89f1701fb6886119a88062cf76fe842ea9e232b9839" -dependencies = [ - "darling", - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 2.0.87", - "thiserror", -] - -[[package]] -name = "sea-schema" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aab1592d17860a9a8584d9b549aebcd06f7bdc3ff615f71752486ba0b05b1e6e" -dependencies = [ - "futures", - "sea-query", - "sea-schema-derive", -] - -[[package]] -name = "sea-schema-derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "debdc8729c37fdbf88472f97fd470393089f997a909e535ff67c544d18cfccf0" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" - -[[package]] -name = "seq-macro" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" - -[[package]] -name = "serde" -version = "1.0.215" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.215" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "serde_json" -version = "1.0.132" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" -dependencies = [ - "base64 0.22.1", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.6.0", - "serde", - "serde_derive", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[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 = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -dependencies = [ - "serde", -] - -[[package]] -name = "snafu" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" -dependencies = [ - "doc-comment", - "snafu-derive", -] - -[[package]] -name = "snafu-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "snap" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" - -[[package]] -name = "socket2" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -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.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" -dependencies = [ - "nom", - "unicode_categories", -] - -[[package]] -name = "sqlparser" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0272b7bb0a225320170c99901b4b5fb3a4384e255a7f2cc228f61e2ba3893e75" -dependencies = [ - "log", - "sqlparser_derive", -] - -[[package]] -name = "sqlparser_derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55fe75cb4a364c7f7ae06c7dbbc8d84bddd85d6cdf9975963c3935bc1991761e" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "sqlx" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93334716a037193fac19df402f8571269c84a00852f6a7066b5d2616dcd64d3e" -dependencies = [ - "sqlx-core", - "sqlx-macros", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", -] - -[[package]] -name = "sqlx-core" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d8060b456358185f7d50c55d9b5066ad956956fddec42ee2e8567134a8936e" -dependencies = [ - "atoi", - "bigdecimal", - "byteorder", - "bytes", - "chrono", - "crc", - "crossbeam-queue", - "either", - "event-listener", - "futures-channel", - "futures-core", - "futures-intrusive", - "futures-io", - "futures-util", - "hashbrown 0.14.5", - "hashlink", - "hex", - "indexmap 2.6.0", - "log", - "memchr", - "once_cell", - "paste", - "percent-encoding", - "rust_decimal", - "rustls", - "rustls-pemfile", - "serde", - "serde_json", - "sha2", - "smallvec", - "sqlformat", - "thiserror", - "time", - "tokio", - "tokio-stream", - "tracing", - "url", - "uuid", - "webpki-roots", -] - -[[package]] -name = "sqlx-macros" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657" -dependencies = [ - "proc-macro2", - "quote", - "sqlx-core", - "sqlx-macros-core", - "syn 2.0.87", -] - -[[package]] -name = "sqlx-macros-core" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1804e8a7c7865599c9c79be146dc8a9fd8cc86935fa641d3ea58e5f0688abaa5" -dependencies = [ - "dotenvy", - "either", - "heck 0.5.0", - "hex", - "once_cell", - "proc-macro2", - "quote", - "serde", - "serde_json", - "sha2", - "sqlx-core", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", - "syn 2.0.87", - "tempfile", - "tokio", - "url", -] - -[[package]] -name = "sqlx-mysql" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" -dependencies = [ - "atoi", - "base64 0.22.1", - "bigdecimal", - "bitflags 2.6.0", - "byteorder", - "bytes", - "chrono", - "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", - "rust_decimal", - "serde", - "sha1", - "sha2", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror", - "time", - "tracing", - "uuid", - "whoami", -] - -[[package]] -name = "sqlx-postgres" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" -dependencies = [ - "atoi", - "base64 0.22.1", - "bigdecimal", - "bitflags 2.6.0", - "byteorder", - "chrono", - "crc", - "dotenvy", - "etcetera", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "hex", - "hkdf", - "hmac", - "home", - "itoa", - "log", - "md-5", - "memchr", - "num-bigint", - "once_cell", - "rand", - "rust_decimal", - "serde", - "serde_json", - "sha2", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror", - "time", - "tracing", - "uuid", - "whoami", -] - -[[package]] -name = "sqlx-sqlite" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680" -dependencies = [ - "atoi", - "chrono", - "flume", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "libsqlite3-sys", - "log", - "percent-encoding", - "serde", - "serde_urlencoded", - "sqlx-core", - "time", - "tracing", - "url", - "uuid", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[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" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" -dependencies = [ - "strum_macros 0.25.3", -] - -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" - -[[package]] -name = "strum_macros" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.87", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.87", -] - -[[package]] -name = "subtle" -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.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" -dependencies = [ - "cfg-if", - "fastrand", - "once_cell", - "rustix", - "windows-sys 0.59.0", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "thrift" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e54bc85fc7faa8bc175c4bab5b92ba8d9a3ce893d0e9f42cc455c8ab16a9e09" -dependencies = [ - "byteorder", - "integer-encoding", - "ordered-float 2.10.1", -] - -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.41.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "socket2", - "tokio-macros", - "windows-sys 0.52.0", -] - -[[package]] -name = "tokio-macros" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "tokio-stream" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" - -[[package]] -name = "toml_edit" -version = "0.22.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" -dependencies = [ - "indexmap 2.6.0", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "matchers", - "once_cell", - "regex", - "sharded-slab", - "thread_local", - "tracing", - "tracing-core", -] - -[[package]] -name = "trait-variant" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "twox-hash" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" -dependencies = [ - "cfg-if", - "static_assertions", -] - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - -[[package]] -name = "unicode-ident" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-properties" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode-width" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" - -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "uuid" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" -dependencies = [ - "getrandom", - "serde", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -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.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.87", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" - -[[package]] -name = "webpki-roots" -version = "0.26.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "whoami" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" -dependencies = [ - "redox_syscall", - "wasite", -] - -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" -dependencies = [ - "memchr", -] - -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "xz2" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" -dependencies = [ - "lzma-sys", -] - -[[package]] -name = "yansi" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" - -[[package]] -name = "yoke" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[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.87", -] - -[[package]] -name = "zerofrom" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" - -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "zstd" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" -dependencies = [ - "zstd-safe 6.0.6", -] - -[[package]] -name = "zstd" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" -dependencies = [ - "zstd-safe 7.2.1", -] - -[[package]] -name = "zstd-safe" -version = "6.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-safe" -version = "7.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/optd-cost-model/Cargo.toml b/optd-cost-model/Cargo.toml deleted file mode 100644 index 1812f8d..0000000 --- a/optd-cost-model/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "optd-cost-model" -version = "0.1.0" -edition = "2021" -authors = ["Yuanxin Cao", "Lan Lou", "Kunle Li"] - -[dependencies] -optd-persistent = { path = "../optd-persistent", version = "0.1" } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -serde_with = { version = "3.7.0", features = ["json"] } -arrow-schema = "53.2.0" -datafusion-expr = "32.0.0" -datafusion = "32.0.0" -ordered-float = "4.0" -chrono = "0.4" -itertools = "0.13" -assert_approx_eq = "1.1.0" -trait-variant = "0.1.2" -tokio = { version = "1.0.1", features = ["macros", "rt-multi-thread"] } -async-trait = "0.1" - -[dev-dependencies] -crossbeam = "0.8" -rand = "0.8" -test-case = "3.3" diff --git a/optd-cost-model/src/common/mod.rs b/optd-cost-model/src/common/mod.rs deleted file mode 100644 index ca5eb7e..0000000 --- a/optd-cost-model/src/common/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod nodes; -pub mod predicates; -pub mod properties; -pub mod types; -pub mod values; diff --git a/optd-cost-model/src/common/nodes.rs b/optd-cost-model/src/common/nodes.rs deleted file mode 100644 index 79a47f7..0000000 --- a/optd-cost-model/src/common/nodes.rs +++ /dev/null @@ -1,127 +0,0 @@ -use core::fmt; -use std::{fmt::Display, sync::Arc}; - -use arrow_schema::DataType; - -use super::{ - predicates::{ - bin_op_pred::BinOpType, constant_pred::ConstantType, func_pred::FuncType, - log_op_pred::LogOpType, sort_order_pred::SortOrderType, un_op_pred::UnOpType, - }, - values::Value, -}; - -/// TODO: documentation -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum JoinType { - Inner = 1, - FullOuter, - LeftOuter, - RightOuter, - Cross, - LeftSemi, - RightSemi, - LeftAnti, - RightAnti, -} - -impl Display for JoinType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -/// TODO: documentation -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum PhysicalNodeType { - PhysicalProjection, - PhysicalFilter, - PhysicalScan, - PhysicalSort, - PhysicalAgg, - PhysicalHashJoin(JoinType), - PhysicalNestedLoopJoin(JoinType), - PhysicalEmptyRelation, - PhysicalLimit, -} - -impl std::fmt::Display for PhysicalNodeType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self) - } -} - -/// TODO: documentation -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum PredicateType { - List, - Constant(ConstantType), - AttrIndex, - UnOp(UnOpType), - BinOp(BinOpType), - LogOp(LogOpType), - Func(FuncType), - SortOrder(SortOrderType), - Between, - Cast, - Like, - DataType(DataType), - InList, -} - -impl std::fmt::Display for PredicateType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self) - } -} - -pub type ArcPredicateNode = Arc; - -/// TODO: documentation -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub struct PredicateNode { - /// A generic predicate node type - pub typ: PredicateType, - /// Child predicate nodes, always materialized - pub children: Vec, - /// Data associated with the predicate, if any - pub data: Option, -} - -impl std::fmt::Display for PredicateNode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "({}", self.typ)?; - for child in &self.children { - write!(f, " {}", child)?; - } - if let Some(data) = &self.data { - write!(f, " {}", data)?; - } - write!(f, ")") - } -} - -impl PredicateNode { - pub fn child(&self, idx: usize) -> ArcPredicateNode { - self.children[idx].clone() - } - - pub fn unwrap_data(&self) -> Value { - self.data.clone().unwrap() - } -} -pub trait ReprPredicateNode: 'static + Clone { - fn into_pred_node(self) -> ArcPredicateNode; - - fn from_pred_node(pred_node: ArcPredicateNode) -> Option; -} - -impl ReprPredicateNode for ArcPredicateNode { - fn into_pred_node(self) -> ArcPredicateNode { - self - } - - fn from_pred_node(pred_node: ArcPredicateNode) -> Option { - Some(pred_node) - } -} diff --git a/optd-cost-model/src/common/predicates/attr_index_pred.rs b/optd-cost-model/src/common/predicates/attr_index_pred.rs deleted file mode 100644 index 412c7a3..0000000 --- a/optd-cost-model/src/common/predicates/attr_index_pred.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::common::{ - nodes::{ArcPredicateNode, PredicateNode, PredicateType, ReprPredicateNode}, - values::Value, -}; - -/// [`AttributeIndexPred`] represents the position of an attribute in a schema or -/// [`GroupAttrRefs`]. -/// -/// The `data` field holds the index of the attribute in the schema or [`GroupAttrRefs`]. -#[derive(Clone, Debug)] -pub struct AttrIndexPred(pub ArcPredicateNode); - -impl AttrIndexPred { - pub fn new(attr_idx: u64) -> AttrIndexPred { - AttrIndexPred( - PredicateNode { - typ: PredicateType::AttrIndex, - children: vec![], - data: Some(Value::UInt64(attr_idx)), - } - .into(), - ) - } - - /// Gets the attribute index. - pub fn attr_index(&self) -> u64 { - self.0.data.as_ref().unwrap().as_u64() - } -} - -impl ReprPredicateNode for AttrIndexPred { - fn into_pred_node(self) -> ArcPredicateNode { - self.0 - } - - fn from_pred_node(pred_node: ArcPredicateNode) -> Option { - if pred_node.typ != PredicateType::AttrIndex { - return None; - } - Some(Self(pred_node)) - } -} diff --git a/optd-cost-model/src/common/predicates/bin_op_pred.rs b/optd-cost-model/src/common/predicates/bin_op_pred.rs deleted file mode 100644 index 5c48688..0000000 --- a/optd-cost-model/src/common/predicates/bin_op_pred.rs +++ /dev/null @@ -1,87 +0,0 @@ -use crate::common::nodes::{ArcPredicateNode, PredicateNode, PredicateType, ReprPredicateNode}; - -/// TODO: documentation -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub enum BinOpType { - // numerical - Add, - Sub, - Mul, - Div, - Mod, - - // comparison - Eq, - Neq, - Gt, - Lt, - Geq, - Leq, -} - -impl std::fmt::Display for BinOpType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self) - } -} - -impl BinOpType { - pub fn is_numerical(&self) -> bool { - matches!( - self, - Self::Add | Self::Sub | Self::Mul | Self::Div | Self::Mod - ) - } - - pub fn is_comparison(&self) -> bool { - matches!( - self, - Self::Eq | Self::Neq | Self::Gt | Self::Lt | Self::Geq | Self::Leq - ) - } -} - -#[derive(Clone, Debug)] -pub struct BinOpPred(pub ArcPredicateNode); - -impl BinOpPred { - pub fn new(left: ArcPredicateNode, right: ArcPredicateNode, op_type: BinOpType) -> Self { - BinOpPred( - PredicateNode { - typ: PredicateType::BinOp(op_type), - children: vec![left, right], - data: None, - } - .into(), - ) - } - - pub fn left_child(&self) -> ArcPredicateNode { - self.0.child(0) - } - - pub fn right_child(&self) -> ArcPredicateNode { - self.0.child(1) - } - - pub fn op_type(&self) -> BinOpType { - if let PredicateType::BinOp(op_type) = self.0.typ { - op_type - } else { - panic!("not a bin op") - } - } -} - -impl ReprPredicateNode for BinOpPred { - fn into_pred_node(self) -> ArcPredicateNode { - self.0 - } - - fn from_pred_node(pred_node: ArcPredicateNode) -> Option { - if !matches!(pred_node.typ, PredicateType::BinOp(_)) { - return None; - } - Some(Self(pred_node)) - } -} diff --git a/optd-cost-model/src/common/predicates/cast_pred.rs b/optd-cost-model/src/common/predicates/cast_pred.rs deleted file mode 100644 index 2e1ef54..0000000 --- a/optd-cost-model/src/common/predicates/cast_pred.rs +++ /dev/null @@ -1,49 +0,0 @@ -use arrow_schema::DataType; - -use crate::common::nodes::{ArcPredicateNode, PredicateNode, PredicateType, ReprPredicateNode}; - -use super::data_type_pred::DataTypePred; - -/// [`CastPred`] casts a column from one data type to another. -/// -/// A [`CastPred`] has two children: -/// 1. The original data to cast -/// 2. The target data type to cast to -#[derive(Clone, Debug)] -pub struct CastPred(pub ArcPredicateNode); - -impl CastPred { - pub fn new(child: ArcPredicateNode, cast_to: DataType) -> Self { - CastPred( - PredicateNode { - typ: PredicateType::Cast, - children: vec![child, DataTypePred::new(cast_to).into_pred_node()], - data: None, - } - .into(), - ) - } - - pub fn child(&self) -> ArcPredicateNode { - self.0.child(0) - } - - pub fn cast_to(&self) -> DataType { - DataTypePred::from_pred_node(self.0.child(1)) - .unwrap() - .data_type() - } -} - -impl ReprPredicateNode for CastPred { - fn into_pred_node(self) -> ArcPredicateNode { - self.0 - } - - fn from_pred_node(pred_node: ArcPredicateNode) -> Option { - if !matches!(pred_node.typ, PredicateType::Cast) { - return None; - } - Some(Self(pred_node)) - } -} diff --git a/optd-cost-model/src/common/predicates/constant_pred.rs b/optd-cost-model/src/common/predicates/constant_pred.rs deleted file mode 100644 index 61285f7..0000000 --- a/optd-cost-model/src/common/predicates/constant_pred.rs +++ /dev/null @@ -1,220 +0,0 @@ -use std::sync::Arc; - -use arrow_schema::{DataType, IntervalUnit}; -use optd_persistent::cost_model::interface::AttrType; -use serde::{Deserialize, Serialize}; - -use crate::common::{ - nodes::{ArcPredicateNode, PredicateNode, PredicateType, ReprPredicateNode}, - values::{SerializableOrderedF64, Value}, -}; - -/// TODO: documentation -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] -pub enum ConstantType { - Bool, - Utf8String, - UInt8, - UInt16, - UInt32, - UInt64, - Int8, - Int16, - Int32, - Int64, - Float64, - Date, - IntervalMonthDateNano, - Decimal, - Binary, -} - -impl ConstantType { - pub fn get_data_type_from_value(value: &Value) -> Self { - match value { - Value::Bool(_) => ConstantType::Bool, - Value::String(_) => ConstantType::Utf8String, - Value::UInt8(_) => ConstantType::UInt8, - Value::UInt16(_) => ConstantType::UInt16, - Value::UInt32(_) => ConstantType::UInt32, - Value::UInt64(_) => ConstantType::UInt64, - Value::Int8(_) => ConstantType::Int8, - Value::Int16(_) => ConstantType::Int16, - Value::Int32(_) => ConstantType::Int32, - Value::Int64(_) => ConstantType::Int64, - Value::Float(_) => ConstantType::Float64, - Value::Date32(_) => ConstantType::Date, - _ => unimplemented!("get_data_type_from_value() not implemented for value {value}"), - } - } - - // TODO: current DataType and ConstantType are not 1 to 1 mapping - // optd schema stores constantType from data type in catalog.get - // for decimal128, the precision is lost - pub fn from_data_type(data_type: DataType) -> Self { - match data_type { - DataType::Binary => ConstantType::Binary, - DataType::Boolean => ConstantType::Bool, - DataType::UInt8 => ConstantType::UInt8, - DataType::UInt16 => ConstantType::UInt16, - DataType::UInt32 => ConstantType::UInt32, - DataType::UInt64 => ConstantType::UInt64, - DataType::Int8 => ConstantType::Int8, - DataType::Int16 => ConstantType::Int16, - DataType::Int32 => ConstantType::Int32, - DataType::Int64 => ConstantType::Int64, - DataType::Float64 => ConstantType::Float64, - DataType::Date32 => ConstantType::Date, - DataType::Interval(IntervalUnit::MonthDayNano) => ConstantType::IntervalMonthDateNano, - DataType::Utf8 => ConstantType::Utf8String, - DataType::Decimal128(_, _) => ConstantType::Decimal, - _ => unimplemented!("no conversion to ConstantType for DataType {data_type}"), - } - } - - pub fn into_data_type(&self) -> DataType { - match self { - ConstantType::Binary => DataType::Binary, - ConstantType::Bool => DataType::Boolean, - ConstantType::UInt8 => DataType::UInt8, - ConstantType::UInt16 => DataType::UInt16, - ConstantType::UInt32 => DataType::UInt32, - ConstantType::UInt64 => DataType::UInt64, - ConstantType::Int8 => DataType::Int8, - ConstantType::Int16 => DataType::Int16, - ConstantType::Int32 => DataType::Int32, - ConstantType::Int64 => DataType::Int64, - ConstantType::Float64 => DataType::Float64, - ConstantType::Date => DataType::Date32, - ConstantType::IntervalMonthDateNano => DataType::Interval(IntervalUnit::MonthDayNano), - ConstantType::Decimal => DataType::Float64, - ConstantType::Utf8String => DataType::Utf8, - } - } - - pub fn from_persistent_attr_type(attr_type: AttrType) -> Self { - match attr_type { - AttrType::Integer => ConstantType::Int32, - AttrType::Float => ConstantType::Float64, - AttrType::Varchar => ConstantType::Utf8String, - AttrType::Boolean => ConstantType::Bool, - } - } -} - -#[derive(Clone, Debug)] -pub struct ConstantPred(pub ArcPredicateNode); - -impl ConstantPred { - pub fn new(value: Value) -> Self { - let typ = ConstantType::get_data_type_from_value(&value); - Self::new_with_type(value, typ) - } - - pub fn new_with_type(value: Value, typ: ConstantType) -> Self { - ConstantPred( - PredicateNode { - typ: PredicateType::Constant(typ), - children: vec![], - data: Some(value), - } - .into(), - ) - } - - pub fn bool(value: bool) -> Self { - Self::new_with_type(Value::Bool(value), ConstantType::Bool) - } - - pub fn string(value: impl AsRef) -> Self { - Self::new_with_type( - Value::String(value.as_ref().into()), - ConstantType::Utf8String, - ) - } - - pub fn uint8(value: u8) -> Self { - Self::new_with_type(Value::UInt8(value), ConstantType::UInt8) - } - - pub fn uint16(value: u16) -> Self { - Self::new_with_type(Value::UInt16(value), ConstantType::UInt16) - } - - pub fn uint32(value: u32) -> Self { - Self::new_with_type(Value::UInt32(value), ConstantType::UInt32) - } - - pub fn uint64(value: u64) -> Self { - Self::new_with_type(Value::UInt64(value), ConstantType::UInt64) - } - - pub fn int8(value: i8) -> Self { - Self::new_with_type(Value::Int8(value), ConstantType::Int8) - } - - pub fn int16(value: i16) -> Self { - Self::new_with_type(Value::Int16(value), ConstantType::Int16) - } - - pub fn int32(value: i32) -> Self { - Self::new_with_type(Value::Int32(value), ConstantType::Int32) - } - - pub fn int64(value: i64) -> Self { - Self::new_with_type(Value::Int64(value), ConstantType::Int64) - } - - pub fn interval_month_day_nano(value: i128) -> Self { - Self::new_with_type(Value::Int128(value), ConstantType::IntervalMonthDateNano) - } - - pub fn float64(value: f64) -> Self { - Self::new_with_type( - Value::Float(SerializableOrderedF64(value.into())), - ConstantType::Float64, - ) - } - - pub fn date(value: i64) -> Self { - Self::new_with_type(Value::Int64(value), ConstantType::Date) - } - - pub fn decimal(value: f64) -> Self { - Self::new_with_type( - Value::Float(SerializableOrderedF64(value.into())), - ConstantType::Decimal, - ) - } - - pub fn serialized(value: Arc<[u8]>) -> Self { - Self::new_with_type(Value::Serialized(value), ConstantType::Binary) - } - - /// Gets the constant value. - pub fn value(&self) -> Value { - self.0.data.clone().unwrap() - } - - pub fn constant_type(&self) -> ConstantType { - if let PredicateType::Constant(typ) = self.0.typ { - typ - } else { - panic!("not a constant") - } - } -} - -impl ReprPredicateNode for ConstantPred { - fn into_pred_node(self) -> ArcPredicateNode { - self.0 - } - - fn from_pred_node(rel_node: ArcPredicateNode) -> Option { - if let PredicateType::Constant(_) = rel_node.typ { - Some(Self(rel_node)) - } else { - None - } - } -} diff --git a/optd-cost-model/src/common/predicates/data_type_pred.rs b/optd-cost-model/src/common/predicates/data_type_pred.rs deleted file mode 100644 index fe29336..0000000 --- a/optd-cost-model/src/common/predicates/data_type_pred.rs +++ /dev/null @@ -1,40 +0,0 @@ -use arrow_schema::DataType; - -use crate::common::nodes::{ArcPredicateNode, PredicateNode, PredicateType, ReprPredicateNode}; - -#[derive(Clone, Debug)] -pub struct DataTypePred(pub ArcPredicateNode); - -impl DataTypePred { - pub fn new(typ: DataType) -> Self { - DataTypePred( - PredicateNode { - typ: PredicateType::DataType(typ), - children: vec![], - data: None, - } - .into(), - ) - } - - pub fn data_type(&self) -> DataType { - if let PredicateType::DataType(ref data_type) = self.0.typ { - data_type.clone() - } else { - panic!("not a data type") - } - } -} - -impl ReprPredicateNode for DataTypePred { - fn into_pred_node(self) -> ArcPredicateNode { - self.0 - } - - fn from_pred_node(pred_node: ArcPredicateNode) -> Option { - if !matches!(pred_node.typ, PredicateType::DataType(_)) { - return None; - } - Some(Self(pred_node)) - } -} diff --git a/optd-cost-model/src/common/predicates/func_pred.rs b/optd-cost-model/src/common/predicates/func_pred.rs deleted file mode 100644 index 7a5b84f..0000000 --- a/optd-cost-model/src/common/predicates/func_pred.rs +++ /dev/null @@ -1,23 +0,0 @@ -/// TODO: documentation -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub enum FuncType { - Scalar(datafusion_expr::BuiltinScalarFunction), - Agg(datafusion_expr::AggregateFunction), - Case, -} - -impl std::fmt::Display for FuncType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self) - } -} - -impl FuncType { - pub fn new_scalar(func_id: datafusion_expr::BuiltinScalarFunction) -> Self { - FuncType::Scalar(func_id) - } - - pub fn new_agg(func_id: datafusion_expr::AggregateFunction) -> Self { - FuncType::Agg(func_id) - } -} diff --git a/optd-cost-model/src/common/predicates/in_list_pred.rs b/optd-cost-model/src/common/predicates/in_list_pred.rs deleted file mode 100644 index 8d3b511..0000000 --- a/optd-cost-model/src/common/predicates/in_list_pred.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::common::{ - nodes::{ArcPredicateNode, PredicateNode, PredicateType, ReprPredicateNode}, - values::Value, -}; - -use super::list_pred::ListPred; - -#[derive(Clone, Debug)] -pub struct InListPred(pub ArcPredicateNode); - -impl InListPred { - pub fn new(child: ArcPredicateNode, list: ListPred, negated: bool) -> Self { - InListPred( - PredicateNode { - typ: PredicateType::InList, - children: vec![child, list.into_pred_node()], - data: Some(Value::Bool(negated)), - } - .into(), - ) - } - - pub fn child(&self) -> ArcPredicateNode { - self.0.child(0) - } - - pub fn list(&self) -> ListPred { - ListPred::from_pred_node(self.0.child(1)).unwrap() - } - - /// `true` for `NOT IN`. - pub fn negated(&self) -> bool { - self.0.data.as_ref().unwrap().as_bool() - } -} - -impl ReprPredicateNode for InListPred { - fn into_pred_node(self) -> ArcPredicateNode { - self.0 - } - - fn from_pred_node(pred_node: ArcPredicateNode) -> Option { - if !matches!(pred_node.typ, PredicateType::InList) { - return None; - } - Some(Self(pred_node)) - } -} diff --git a/optd-cost-model/src/common/predicates/like_pred.rs b/optd-cost-model/src/common/predicates/like_pred.rs deleted file mode 100644 index bf9fe31..0000000 --- a/optd-cost-model/src/common/predicates/like_pred.rs +++ /dev/null @@ -1,66 +0,0 @@ -use std::sync::Arc; - -use crate::common::{ - nodes::{ArcPredicateNode, PredicateNode, PredicateType, ReprPredicateNode}, - values::Value, -}; - -#[derive(Clone, Debug)] -pub struct LikePred(pub ArcPredicateNode); - -impl LikePred { - pub fn new( - negated: bool, - case_insensitive: bool, - child: ArcPredicateNode, - pattern: ArcPredicateNode, - ) -> Self { - // TODO: support multiple values in data. - let negated = if negated { 1 } else { 0 }; - let case_insensitive = if case_insensitive { 1 } else { 0 }; - LikePred( - PredicateNode { - typ: PredicateType::Like, - children: vec![child.into_pred_node(), pattern.into_pred_node()], - data: Some(Value::Serialized(Arc::new([negated, case_insensitive]))), - } - .into(), - ) - } - - pub fn child(&self) -> ArcPredicateNode { - self.0.child(0) - } - - pub fn pattern(&self) -> ArcPredicateNode { - self.0.child(1) - } - - /// `true` for `NOT LIKE`. - pub fn negated(&self) -> bool { - match self.0.data.as_ref().unwrap() { - Value::Serialized(data) => data[0] != 0, - _ => panic!("not a serialized value"), - } - } - - pub fn case_insensitive(&self) -> bool { - match self.0.data.as_ref().unwrap() { - Value::Serialized(data) => data[1] != 0, - _ => panic!("not a serialized value"), - } - } -} - -impl ReprPredicateNode for LikePred { - fn into_pred_node(self) -> ArcPredicateNode { - self.0 - } - - fn from_pred_node(pred_node: ArcPredicateNode) -> Option { - if !matches!(pred_node.typ, PredicateType::Like) { - return None; - } - Some(Self(pred_node)) - } -} diff --git a/optd-cost-model/src/common/predicates/list_pred.rs b/optd-cost-model/src/common/predicates/list_pred.rs deleted file mode 100644 index 972598d..0000000 --- a/optd-cost-model/src/common/predicates/list_pred.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::common::nodes::{ArcPredicateNode, PredicateNode, PredicateType, ReprPredicateNode}; - -#[derive(Clone, Debug)] -pub struct ListPred(pub ArcPredicateNode); - -impl ListPred { - pub fn new(preds: Vec) -> Self { - ListPred( - PredicateNode { - typ: PredicateType::List, - children: preds, - data: None, - } - .into(), - ) - } - - /// Gets number of expressions in the list - pub fn len(&self) -> usize { - self.0.children.len() - } - - pub fn is_empty(&self) -> bool { - self.0.children.is_empty() - } - - pub fn child(&self, idx: usize) -> ArcPredicateNode { - self.0.child(idx) - } - - pub fn to_vec(&self) -> Vec { - self.0.children.clone() - } -} - -impl ReprPredicateNode for ListPred { - fn into_pred_node(self) -> ArcPredicateNode { - self.0 - } - - fn from_pred_node(pred_node: ArcPredicateNode) -> Option { - if pred_node.typ != PredicateType::List { - return None; - } - Some(Self(pred_node)) - } -} diff --git a/optd-cost-model/src/common/predicates/log_op_pred.rs b/optd-cost-model/src/common/predicates/log_op_pred.rs deleted file mode 100644 index 1899cb1..0000000 --- a/optd-cost-model/src/common/predicates/log_op_pred.rs +++ /dev/null @@ -1,85 +0,0 @@ -use std::fmt::Display; - -use crate::common::nodes::{ArcPredicateNode, PredicateNode, PredicateType, ReprPredicateNode}; - -use super::list_pred::ListPred; - -/// TODO: documentation -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub enum LogOpType { - And, - Or, -} - -impl Display for LogOpType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self) - } -} - -#[derive(Clone, Debug)] -pub struct LogOpPred(pub ArcPredicateNode); - -impl LogOpPred { - pub fn new(op_type: LogOpType, preds: Vec) -> Self { - LogOpPred( - PredicateNode { - typ: PredicateType::LogOp(op_type), - children: preds, - data: None, - } - .into(), - ) - } - - /// flatten_nested_logical is a helper function to flatten nested logical operators with same op - /// type eg. (a AND (b AND c)) => ExprList([a, b, c]) - /// (a OR (b OR c)) => ExprList([a, b, c]) - /// It assume the children of the input expr_list are already flattened - /// and can only be used in bottom up manner - pub fn new_flattened_nested_logical(op: LogOpType, expr_list: ListPred) -> Self { - // Since we assume that we are building the children bottom up, - // there is no need to call flatten_nested_logical recursively - let mut new_expr_list = Vec::new(); - for child in expr_list.to_vec() { - if let PredicateType::LogOp(child_op) = child.typ { - if child_op == op { - let child_log_op_expr = LogOpPred::from_pred_node(child).unwrap(); - new_expr_list.extend(child_log_op_expr.children().to_vec()); - continue; - } - } - new_expr_list.push(child.clone()); - } - LogOpPred::new(op, new_expr_list) - } - - pub fn children(&self) -> Vec { - self.0.children.clone() - } - - pub fn child(&self, idx: usize) -> ArcPredicateNode { - self.0.child(idx) - } - - pub fn op_type(&self) -> LogOpType { - if let PredicateType::LogOp(op_type) = self.0.typ { - op_type - } else { - panic!("not a log op") - } - } -} - -impl ReprPredicateNode for LogOpPred { - fn into_pred_node(self) -> ArcPredicateNode { - self.0 - } - - fn from_pred_node(pred_node: ArcPredicateNode) -> Option { - if !matches!(pred_node.typ, PredicateType::LogOp(_)) { - return None; - } - Some(Self(pred_node)) - } -} diff --git a/optd-cost-model/src/common/predicates/mod.rs b/optd-cost-model/src/common/predicates/mod.rs deleted file mode 100644 index 40c64cf..0000000 --- a/optd-cost-model/src/common/predicates/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub mod attr_index_pred; -pub mod bin_op_pred; -pub mod cast_pred; -pub mod constant_pred; -pub mod data_type_pred; -pub mod func_pred; -pub mod in_list_pred; -pub mod like_pred; -pub mod list_pred; -pub mod log_op_pred; -pub mod sort_order_pred; -pub mod un_op_pred; diff --git a/optd-cost-model/src/common/predicates/sort_order_pred.rs b/optd-cost-model/src/common/predicates/sort_order_pred.rs deleted file mode 100644 index 46111f8..0000000 --- a/optd-cost-model/src/common/predicates/sort_order_pred.rs +++ /dev/null @@ -1,14 +0,0 @@ -use std::fmt::Display; - -/// TODO: documentation -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub enum SortOrderType { - Asc, - Desc, -} - -impl Display for SortOrderType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self) - } -} diff --git a/optd-cost-model/src/common/predicates/un_op_pred.rs b/optd-cost-model/src/common/predicates/un_op_pred.rs deleted file mode 100644 index a3fc270..0000000 --- a/optd-cost-model/src/common/predicates/un_op_pred.rs +++ /dev/null @@ -1,57 +0,0 @@ -use std::fmt::Display; - -use crate::common::nodes::{ArcPredicateNode, PredicateNode, PredicateType, ReprPredicateNode}; - -/// TODO: documentation -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub enum UnOpType { - Neg = 1, - Not, -} - -impl Display for UnOpType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self) - } -} - -#[derive(Clone, Debug)] -pub struct UnOpPred(pub ArcPredicateNode); - -impl UnOpPred { - pub fn new(child: ArcPredicateNode, op_type: UnOpType) -> Self { - UnOpPred( - PredicateNode { - typ: PredicateType::UnOp(op_type), - children: vec![child], - data: None, - } - .into(), - ) - } - - pub fn child(&self) -> ArcPredicateNode { - self.0.child(0) - } - - pub fn op_type(&self) -> UnOpType { - if let PredicateType::UnOp(op_type) = self.0.typ { - op_type - } else { - panic!("not a un op") - } - } -} - -impl ReprPredicateNode for UnOpPred { - fn into_pred_node(self) -> ArcPredicateNode { - self.0 - } - - fn from_pred_node(pred_node: ArcPredicateNode) -> Option { - if !matches!(pred_node.typ, PredicateType::UnOp(_)) { - return None; - } - Some(Self(pred_node)) - } -} diff --git a/optd-cost-model/src/common/properties/attr_ref.rs b/optd-cost-model/src/common/properties/attr_ref.rs deleted file mode 100644 index d6105b6..0000000 --- a/optd-cost-model/src/common/properties/attr_ref.rs +++ /dev/null @@ -1,249 +0,0 @@ -use std::collections::HashSet; - -use crate::{common::types::TableId, utils::DisjointSets}; - -pub type AttrRefs = Vec; - -/// [`BaseTableAttrRef`] represents a reference to an attribute in a base table, -/// i.e. a table existing in the catalog. -#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] -pub struct BaseTableAttrRef { - pub table_id: TableId, - pub attr_idx: u64, -} - -/// [`AttrRef`] represents a reference to an attribute in a query. -#[derive(Clone, Debug)] -pub enum AttrRef { - /// Reference to a base table attribute. - BaseTableAttrRef(BaseTableAttrRef), - /// Reference to a derived attribute (e.g. t.v1 + t.v2). - /// TODO: Better representation of derived attributes. - Derived, -} - -impl AttrRef { - pub fn new_base_table_attr_ref(table_id: TableId, attr_idx: u64) -> Self { - AttrRef::BaseTableAttrRef(BaseTableAttrRef { table_id, attr_idx }) - } - - pub fn base_table_attr_ref(table_id: TableId, attr_idx: u64) -> Self { - AttrRef::BaseTableAttrRef(BaseTableAttrRef { table_id, attr_idx }) - } -} - -impl From for AttrRef { - fn from(attr: BaseTableAttrRef) -> Self { - AttrRef::BaseTableAttrRef(attr) - } -} - -/// [`EqPredicate`] represents an equality predicate between two attributes. -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct EqPredicate { - pub left: BaseTableAttrRef, - pub right: BaseTableAttrRef, -} - -impl EqPredicate { - pub fn new(left: BaseTableAttrRef, right: BaseTableAttrRef) -> Self { - Self { left, right } - } -} - -/// [`SemanticCorrelation`] represents the semantic correlation between attributes in a -/// query. "Semantic" means that the attributes are correlated based on the -/// semantics of the query, not the statistics. -/// -/// [`SemanticCorrelation`] contains equal attributes denoted by disjoint sets of base -/// table attributes, e.g. {{ t1.c1 = t2.c1 = t3.c1 }, { t1.c2 = t2.c2 }}. -#[derive(Clone, Debug, Default)] -pub struct SemanticCorrelation { - /// A disjoint set of base table attributes with equal values in the same row. - disjoint_eq_attr_sets: DisjointSets, - /// The predicates that define the equalities. - eq_predicates: HashSet, -} - -impl SemanticCorrelation { - pub fn new() -> Self { - Self { - disjoint_eq_attr_sets: DisjointSets::new(), - eq_predicates: HashSet::new(), - } - } - - pub fn add_predicate(&mut self, predicate: EqPredicate) { - let left = &predicate.left; - let right = &predicate.right; - - // Add the indices to the set if they do not exist. - if !self.disjoint_eq_attr_sets.contains(left) { - self.disjoint_eq_attr_sets - .make_set(left.clone()) - .expect("just checked left attribute index does not exist"); - } - if !self.disjoint_eq_attr_sets.contains(right) { - self.disjoint_eq_attr_sets - .make_set(right.clone()) - .expect("just checked right attribute index does not exist"); - } - // Union the attributes. - self.disjoint_eq_attr_sets - .union(left, right) - .expect("both attribute indices should exist"); - - // Keep track of the predicate. - self.eq_predicates.insert(predicate); - } - - /// Determine if two attributes are in the same set. - pub fn is_eq(&mut self, left: &BaseTableAttrRef, right: &BaseTableAttrRef) -> bool { - self.disjoint_eq_attr_sets - .same_set(left, right) - .unwrap_or(false) - } - - pub fn contains(&self, base_attr_ref: &BaseTableAttrRef) -> bool { - self.disjoint_eq_attr_sets.contains(base_attr_ref) - } - - /// Get the number of attributes that are equal to `attr`, including `attr` itself. - pub fn num_eq_attributes(&mut self, attr: &BaseTableAttrRef) -> usize { - self.disjoint_eq_attr_sets.set_size(attr).unwrap() - } - - /// Find the set of predicates that define the equality of the set of attributes `attr` belongs to. - pub fn find_predicates_for_eq_attr_set(&mut self, attr: &BaseTableAttrRef) -> Vec { - let mut predicates = Vec::new(); - for predicate in &self.eq_predicates { - let left = &predicate.left; - let right = &predicate.right; - if (left != attr && self.disjoint_eq_attr_sets.same_set(attr, left).unwrap()) - || (right != attr && self.disjoint_eq_attr_sets.same_set(attr, right).unwrap()) - { - predicates.push(predicate.clone()); - } - } - predicates - } - - /// Find the set of attributes that define the equality of the set of attributes `attr` belongs to. - pub fn find_attrs_for_eq_attribute_set( - &mut self, - attr: &BaseTableAttrRef, - ) -> HashSet { - let predicates = self.find_predicates_for_eq_attr_set(attr); - predicates - .into_iter() - .flat_map(|predicate| vec![predicate.left, predicate.right]) - .collect() - } - - /// Union two `EqBaseTableattributesets` to produce a new disjoint sets. - pub fn union(x: Self, y: Self) -> Self { - let mut eq_attr_sets = Self::new(); - for predicate in x - .eq_predicates - .into_iter() - .chain(y.eq_predicates.into_iter()) - { - eq_attr_sets.add_predicate(predicate); - } - eq_attr_sets - } - - pub fn merge(x: Option, y: Option) -> Option { - let eq_attr_sets = match (x, y) { - (Some(x), Some(y)) => Self::union(x, y), - (Some(x), None) => x.clone(), - (None, Some(y)) => y.clone(), - _ => return None, - }; - Some(eq_attr_sets) - } -} - -/// [`GroupAttrRefs`] represents the attributes of a group in a query. -#[derive(Clone, Debug, Default)] -pub struct GroupAttrRefs { - attr_refs: AttrRefs, - /// Correlation of the output attributes of the group. - output_correlation: Option, -} - -impl GroupAttrRefs { - pub fn new(attribute_refs: AttrRefs, output_correlation: Option) -> Self { - Self { - attr_refs: attribute_refs, - output_correlation, - } - } - - pub fn attr_refs(&self) -> &AttrRefs { - &self.attr_refs - } - - pub fn output_correlation(&self) -> Option<&SemanticCorrelation> { - self.output_correlation.as_ref() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_eq_base_table_attribute_sets() { - let attr1 = BaseTableAttrRef { - table_id: TableId(1), - attr_idx: 1, - }; - let attr2 = BaseTableAttrRef { - table_id: TableId(2), - attr_idx: 2, - }; - let attr3 = BaseTableAttrRef { - table_id: TableId(3), - attr_idx: 3, - }; - let attr4 = BaseTableAttrRef { - table_id: TableId(4), - attr_idx: 4, - }; - let pred1 = EqPredicate::new(attr1.clone(), attr2.clone()); - let pred2 = EqPredicate::new(attr3.clone(), attr4.clone()); - let pred3 = EqPredicate::new(attr1.clone(), attr3.clone()); - - let mut eq_attr_sets = SemanticCorrelation::new(); - - // (1, 2) - eq_attr_sets.add_predicate(pred1.clone()); - assert!(eq_attr_sets.is_eq(&attr1, &attr2)); - - // (1, 2), (3, 4) - eq_attr_sets.add_predicate(pred2.clone()); - assert!(eq_attr_sets.is_eq(&attr3, &attr4)); - assert!(!eq_attr_sets.is_eq(&attr2, &attr3)); - - let predicates = eq_attr_sets.find_predicates_for_eq_attr_set(&attr1); - assert_eq!(predicates.len(), 1); - assert!(predicates.contains(&pred1)); - - let predicates = eq_attr_sets.find_predicates_for_eq_attr_set(&attr3); - assert_eq!(predicates.len(), 1); - assert!(predicates.contains(&pred2)); - - // (1, 2, 3, 4) - eq_attr_sets.add_predicate(pred3.clone()); - assert!(eq_attr_sets.is_eq(&attr1, &attr3)); - assert!(eq_attr_sets.is_eq(&attr2, &attr4)); - assert!(eq_attr_sets.is_eq(&attr1, &attr4)); - - let predicates = eq_attr_sets.find_predicates_for_eq_attr_set(&attr1); - assert_eq!(predicates.len(), 3); - assert!(predicates.contains(&pred1)); - assert!(predicates.contains(&pred2)); - assert!(predicates.contains(&pred3)); - } -} diff --git a/optd-cost-model/src/common/properties/mod.rs b/optd-cost-model/src/common/properties/mod.rs deleted file mode 100644 index a90d634..0000000 --- a/optd-cost-model/src/common/properties/mod.rs +++ /dev/null @@ -1,41 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use super::predicates::constant_pred::ConstantType; - -pub mod attr_ref; -pub mod schema; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Attribute { - pub name: String, - pub typ: ConstantType, - pub nullable: bool, -} - -impl std::fmt::Display for Attribute { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.nullable { - write!(f, "{}:{:?}", self.name, self.typ) - } else { - write!(f, "{}:{:?}(non-null)", self.name, self.typ) - } - } -} - -impl Attribute { - pub fn new(name: String, typ: ConstantType, nullable: bool) -> Self { - Self { - name, - typ, - nullable, - } - } - - pub fn new_non_null_int64(name: String) -> Self { - Self { - name, - typ: ConstantType::Int64, - nullable: false, - } - } -} diff --git a/optd-cost-model/src/common/properties/schema.rs b/optd-cost-model/src/common/properties/schema.rs deleted file mode 100644 index d25a23a..0000000 --- a/optd-cost-model/src/common/properties/schema.rs +++ /dev/null @@ -1,41 +0,0 @@ -use itertools::Itertools; - -use serde::{Deserialize, Serialize}; - -use super::Attribute; - -/// [`Schema`] represents the schema of a group in the memo. It contains a list of attributes. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Schema { - pub attributes: Vec, -} - -impl std::fmt::Display for Schema { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "[{}]", - self.attributes.iter().map(|x| x.to_string()).join(", ") - ) - } -} - -impl Schema { - pub fn new(attributes: Vec) -> Self { - Self { attributes } - } - - pub fn len(&self) -> usize { - self.attributes.len() - } - - pub fn is_empty(&self) -> bool { - self.len() == 0 - } -} - -impl From> for Schema { - fn from(attributes: Vec) -> Self { - Self::new(attributes) - } -} diff --git a/optd-cost-model/src/common/types.rs b/optd-cost-model/src/common/types.rs deleted file mode 100644 index fecd143..0000000 --- a/optd-cost-model/src/common/types.rs +++ /dev/null @@ -1,84 +0,0 @@ -use std::fmt::Display; - -/// TODO: Implement from and to methods for the following types to enable conversion -/// to and from their persistent counterparts. - -/// TODO: documentation -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)] -pub struct GroupId(pub u64); - -/// TODO: documentation -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)] -pub struct ExprId(pub u64); - -/// TODO: documentation -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)] -pub struct TableId(pub u64); - -/// TODO: documentation -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)] -pub struct AttrId(pub u64); - -/// TODO: documentation -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)] -pub struct EpochId(pub u64); - -impl Display for GroupId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "!{}", self.0) - } -} - -impl Display for ExprId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl Display for TableId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Table#{}", self.0) - } -} - -impl Display for AttrId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Attr#{}", self.0) - } -} - -impl Display for EpochId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Epoch#{}", self.0) - } -} - -impl From for i32 { - fn from(id: GroupId) -> i32 { - id.0 as i32 - } -} - -impl From for i32 { - fn from(id: ExprId) -> i32 { - id.0 as i32 - } -} - -impl From for i32 { - fn from(id: TableId) -> i32 { - id.0 as i32 - } -} - -impl From for i32 { - fn from(id: AttrId) -> i32 { - id.0 as i32 - } -} - -impl From for i32 { - fn from(id: EpochId) -> i32 { - id.0 as i32 - } -} diff --git a/optd-cost-model/src/common/values.rs b/optd-cost-model/src/common/values.rs deleted file mode 100644 index 7561f0b..0000000 --- a/optd-cost-model/src/common/values.rs +++ /dev/null @@ -1,205 +0,0 @@ -use arrow_schema::DataType; -use chrono::NaiveDate; -use ordered_float::OrderedFloat; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::sync::Arc; - -/// TODO: documentation -#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct SerializableOrderedF64(pub OrderedFloat); - -/// TODO: documentation -impl Serialize for SerializableOrderedF64 { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - // Directly serialize the inner f64 value of the OrderedFloat - self.0 .0.serialize(serializer) - } -} - -/// TODO: documentation -impl<'de> Deserialize<'de> for SerializableOrderedF64 { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - // Deserialize an f64 and wrap it in an OrderedFloat - let float = f64::deserialize(deserializer)?; - Ok(SerializableOrderedF64(OrderedFloat(float))) - } -} - -/// TODO: documentation -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)] -pub enum Value { - UInt8(u8), - UInt16(u16), - UInt32(u32), - UInt64(u64), - Int8(i8), - Int16(i16), - Int32(i32), - Int64(i64), - Int128(i128), - Float(SerializableOrderedF64), - String(Arc), - Bool(bool), - Date32(i32), - Decimal128(i128), - Serialized(Arc<[u8]>), -} - -impl std::fmt::Display for Value { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::UInt8(x) => write!(f, "{x}(u8)"), - Self::UInt16(x) => write!(f, "{x}(u16)"), - Self::UInt32(x) => write!(f, "{x}(u32)"), - Self::UInt64(x) => write!(f, "{x}(u64)"), - Self::Int8(x) => write!(f, "{x}(i8)"), - Self::Int16(x) => write!(f, "{x}(i16)"), - Self::Int32(x) => write!(f, "{x}(i32)"), - Self::Int64(x) => write!(f, "{x}(i64)"), - Self::Int128(x) => write!(f, "{x}(i128)"), - Self::Float(x) => write!(f, "{}(float)", x.0), - Self::String(x) => write!(f, "\"{x}\""), - Self::Bool(x) => write!(f, "{x}"), - Self::Date32(x) => write!(f, "{x}(date32)"), - Self::Decimal128(x) => write!(f, "{x}(decimal128)"), - Self::Serialized(x) => write!(f, "", x.len()), - } - } -} - -/// TODO: documentation -/// The `as_*()` functions do not perform conversions. This is *unlike* the `as` -/// keyword in rust. -/// -/// If you want to perform conversions, use the `to_*()` functions. -impl Value { - pub fn as_u8(&self) -> u8 { - match self { - Value::UInt8(i) => *i, - _ => panic!("Value is not an u8"), - } - } - - pub fn as_u16(&self) -> u16 { - match self { - Value::UInt16(i) => *i, - _ => panic!("Value is not an u16"), - } - } - - pub fn as_u32(&self) -> u32 { - match self { - Value::UInt32(i) => *i, - _ => panic!("Value is not an u32"), - } - } - - pub fn as_u64(&self) -> u64 { - match self { - Value::UInt64(i) => *i, - _ => panic!("Value is not an u64"), - } - } - - pub fn as_i8(&self) -> i8 { - match self { - Value::Int8(i) => *i, - _ => panic!("Value is not an i8"), - } - } - - pub fn as_i16(&self) -> i16 { - match self { - Value::Int16(i) => *i, - _ => panic!("Value is not an i16"), - } - } - - pub fn as_i32(&self) -> i32 { - match self { - Value::Int32(i) => *i, - _ => panic!("Value is not an i32"), - } - } - - pub fn as_i64(&self) -> i64 { - match self { - Value::Int64(i) => *i, - _ => panic!("Value is not an i64"), - } - } - - pub fn as_i128(&self) -> i128 { - match self { - Value::Int128(i) => *i, - _ => panic!("Value is not an i128"), - } - } - - pub fn as_f64(&self) -> f64 { - match self { - Value::Float(i) => *i.0, - _ => panic!("Value is not an f64"), - } - } - - pub fn as_bool(&self) -> bool { - match self { - Value::Bool(i) => *i, - _ => panic!("Value is not a bool"), - } - } - - pub fn as_str(&self) -> Arc { - match self { - Value::String(i) => i.clone(), - _ => panic!("Value is not a string"), - } - } - - pub fn as_slice(&self) -> Arc<[u8]> { - match self { - Value::Serialized(i) => i.clone(), - _ => panic!("Value is not a serialized"), - } - } - - pub fn convert_to_type(&self, typ: DataType) -> Value { - match typ { - DataType::Int32 => Value::Int32(match self { - Value::Int32(i32) => *i32, - Value::Int64(i64) => (*i64).try_into().unwrap(), - _ => panic!("{self} could not be converted into an Int32"), - }), - DataType::Int64 => Value::Int64(match self { - Value::Int64(i64) => *i64, - Value::Int32(i32) => (*i32).into(), - _ => panic!("{self} could not be converted into an Int64"), - }), - DataType::UInt64 => Value::UInt64(match self { - Value::Int64(i64) => (*i64).try_into().unwrap(), - Value::UInt64(i64) => *i64, - Value::UInt32(i32) => (*i32).into(), - _ => panic!("{self} could not be converted into an UInt64"), - }), - DataType::Date32 => Value::Date32(match self { - Value::Date32(date32) => *date32, - Value::String(str) => { - let date = NaiveDate::parse_from_str(str, "%Y-%m-%d").unwrap(); - let epoch = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap(); - let duration_since_epoch = date.signed_duration_since(epoch); - let days_since_epoch: i32 = duration_since_epoch.num_days() as i32; - days_since_epoch - } - _ => panic!("{self} could not be converted into an Date32"), - }), - _ => unimplemented!("Have not implemented convert_to_type for DataType {typ}"), - } - } -} diff --git a/optd-cost-model/src/cost/agg.rs b/optd-cost-model/src/cost/agg.rs deleted file mode 100644 index 62c88d3..0000000 --- a/optd-cost-model/src/cost/agg.rs +++ /dev/null @@ -1,198 +0,0 @@ -use crate::{ - common::{ - nodes::{ArcPredicateNode, PredicateType, ReprPredicateNode}, - predicates::{attr_index_pred::AttrIndexPred, list_pred::ListPred}, - properties::attr_ref::{AttrRef, BaseTableAttrRef}, - types::GroupId, - }, - cost_model::CostModelImpl, - stats::DEFAULT_NUM_DISTINCT, - storage::CostModelStorageManager, - CostModelResult, EstimatedStatistic, SemanticError, -}; - -impl CostModelImpl { - pub async fn get_agg_row_cnt( - &self, - group_id: GroupId, - group_by: ArcPredicateNode, - ) -> CostModelResult { - let group_by = ListPred::from_pred_node(group_by).unwrap(); - if group_by.is_empty() { - Ok(EstimatedStatistic(1.0)) - } else { - // Multiply the n-distinct of all the group by columns. - // TODO: improve with multi-dimensional n-distinct - let mut row_cnt = 1; - - for node in &group_by.0.children { - match node.typ { - PredicateType::AttrIndex => { - let attr_ref = - AttrIndexPred::from_pred_node(node.clone()).ok_or_else(|| { - SemanticError::InvalidPredicate( - "Expected AttributeRef predicate".to_string(), - ) - })?; - if let AttrRef::BaseTableAttrRef(BaseTableAttrRef { table_id, attr_idx }) = - self.memo.get_attribute_ref(group_id, attr_ref.attr_index()) - { - // TODO: Only query ndistinct instead of all kinds of stats. - let stats_option = - self.get_attribute_comb_stats(table_id, &[attr_idx]).await?; - - let ndistinct = match stats_option { - Some(stats) => stats.ndistinct, - None => { - // The column type is not supported or stats are missing. - DEFAULT_NUM_DISTINCT - } - }; - row_cnt *= ndistinct; - } else { - // TOOD: Handle derived attributes. - row_cnt *= DEFAULT_NUM_DISTINCT; - } - } - _ => { - // TODO: Consider the case where `GROUP BY 1`. - panic!("GROUP BY must have attribute ref predicate"); - } - } - } - Ok(EstimatedStatistic(row_cnt as f64)) - } - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use crate::{ - common::predicates::constant_pred::ConstantType, - stats::{utilities::simple_map::SimpleMap, MostCommonValues, DEFAULT_NUM_DISTINCT}, - test_utils::tests::{ - attr_index, create_mock_cost_model_with_attr_types, empty_list, list, - TestPerAttributeStats, TEST_ATTR1_BASE_INDEX, TEST_ATTR2_BASE_INDEX, - TEST_ATTR3_BASE_INDEX, TEST_GROUP1_ID, TEST_TABLE1_ID, - }, - EstimatedStatistic, - }; - - #[tokio::test] - async fn test_agg_no_stats() { - let cost_model = create_mock_cost_model_with_attr_types( - vec![TEST_TABLE1_ID], - vec![], - vec![HashMap::from([ - (TEST_ATTR1_BASE_INDEX, ConstantType::Int32), - (TEST_ATTR2_BASE_INDEX, ConstantType::Int32), - ])], - vec![None], - ); - - // Group by empty list should return 1. - let group_bys = empty_list(); - assert_eq!( - cost_model - .get_agg_row_cnt(TEST_GROUP1_ID, group_bys) - .await - .unwrap(), - EstimatedStatistic(1.0) - ); - - // Group by single column should return the default value since there are no stats. - let group_bys = list(vec![attr_index(0)]); - assert_eq!( - cost_model - .get_agg_row_cnt(TEST_GROUP1_ID, group_bys) - .await - .unwrap(), - EstimatedStatistic(DEFAULT_NUM_DISTINCT as f64) - ); - - // Group by two columns should return the default value squared since there are no stats. - let group_bys = list(vec![attr_index(0), attr_index(1)]); - assert_eq!( - cost_model - .get_agg_row_cnt(TEST_GROUP1_ID, group_bys) - .await - .unwrap(), - EstimatedStatistic((DEFAULT_NUM_DISTINCT * DEFAULT_NUM_DISTINCT) as f64) - ); - } - - #[tokio::test] - async fn test_agg_with_stats() { - let attr1_ndistinct = 12; - let attr2_ndistinct = 645; - let attr1_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::default()), - None, - attr1_ndistinct, - 0.0, - ); - let attr2_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::default()), - None, - attr2_ndistinct, - 0.0, - ); - - let cost_model = create_mock_cost_model_with_attr_types( - vec![TEST_TABLE1_ID], - vec![HashMap::from([ - (TEST_ATTR1_BASE_INDEX, attr1_stats), - (TEST_ATTR2_BASE_INDEX, attr2_stats), - ])], - vec![HashMap::from([ - (TEST_ATTR1_BASE_INDEX, ConstantType::Int32), - (TEST_ATTR2_BASE_INDEX, ConstantType::Int32), - (TEST_ATTR3_BASE_INDEX, ConstantType::Int32), - ])], - vec![None], - ); - - // Group by empty list should return 1. - let group_bys = empty_list(); - assert_eq!( - cost_model - .get_agg_row_cnt(TEST_GROUP1_ID, group_bys) - .await - .unwrap(), - EstimatedStatistic(1.0) - ); - - // Group by single column should return the n-distinct of the column. - let group_bys = list(vec![attr_index(0)]); - assert_eq!( - cost_model - .get_agg_row_cnt(TEST_GROUP1_ID, group_bys) - .await - .unwrap(), - EstimatedStatistic(attr1_ndistinct as f64) - ); - - // Group by two columns should return the product of the n-distinct of the columns. - let group_bys = list(vec![attr_index(0), attr_index(1)]); - assert_eq!( - cost_model - .get_agg_row_cnt(TEST_GROUP1_ID, group_bys) - .await - .unwrap(), - EstimatedStatistic((attr1_ndistinct * attr2_ndistinct) as f64) - ); - - // Group by multiple columns should return the product of the n-distinct of the columns. If one of the columns - // does not have stats, it should use the default value instead. - let group_bys = list(vec![attr_index(0), attr_index(1), attr_index(2)]); - assert_eq!( - cost_model - .get_agg_row_cnt(TEST_GROUP1_ID, group_bys) - .await - .unwrap(), - EstimatedStatistic((attr1_ndistinct * attr2_ndistinct * DEFAULT_NUM_DISTINCT) as f64) - ); - } -} diff --git a/optd-cost-model/src/cost/filter/attribute.rs b/optd-cost-model/src/cost/filter/attribute.rs deleted file mode 100644 index 68dd9a1..0000000 --- a/optd-cost-model/src/cost/filter/attribute.rs +++ /dev/null @@ -1,182 +0,0 @@ -use std::ops::Bound; - -use crate::{ - common::{types::TableId, values::Value}, - cost_model::CostModelImpl, - stats::{AttributeCombValue, AttributeCombValueStats, DEFAULT_EQ_SEL, DEFAULT_INEQ_SEL}, - storage::CostModelStorageManager, - CostModelResult, -}; - -impl CostModelImpl { - /// Get the selectivity of an expression of the form "attribute equals value" (or "value equals - /// attribute") Will handle the case of statistics missing - /// Equality predicates are handled entirely differently from range predicates so this is its - /// own function - /// Also, get_attribute_equality_selectivity is a subroutine when computing range selectivity, - /// which is another reason for separating these into two functions is_eq means whether it's == or != - /// - /// Currently, we only support calculating the equality selectivity for an existed attribute, - /// not a derived attribute. - /// TODO: Support derived attributes. - pub(crate) async fn get_attribute_equality_selectivity( - &self, - table_id: TableId, - attr_base_index: u64, - value: &Value, - is_eq: bool, - ) -> CostModelResult { - let ret_sel = { - if let Some(attribute_stats) = self - .get_attribute_comb_stats(table_id, &[attr_base_index]) - .await? - { - let eq_freq = - if let Some(freq) = attribute_stats.mcvs.freq(&vec![Some(value.clone())]) { - freq - } else { - let non_mcv_freq = 1.0 - attribute_stats.mcvs.total_freq(); - // always safe because usize is at least as large as i32 - let ndistinct_as_usize = attribute_stats.ndistinct as usize; - let non_mcv_cnt = ndistinct_as_usize - attribute_stats.mcvs.cnt(); - if non_mcv_cnt == 0 { - return Ok(0.0); - } - // note that nulls are not included in ndistinct so we don't need to do non_mcv_cnt - // - 1 if null_frac > 0 - (non_mcv_freq - attribute_stats.null_frac) / (non_mcv_cnt as f64) - }; - if is_eq { - eq_freq - } else { - 1.0 - eq_freq - attribute_stats.null_frac - } - } else { - #[allow(clippy::collapsible_else_if)] - if is_eq { - DEFAULT_EQ_SEL - } else { - 1.0 - DEFAULT_EQ_SEL - } - } - }; - - assert!( - (0.0..=1.0).contains(&ret_sel), - "ret_sel ({}) should be in [0, 1]", - ret_sel - ); - Ok(ret_sel) - } - - /// Compute the frequency of values in a attribute less than or equal to the given value. - fn get_attribute_leq_value_freq( - per_attribute_stats: &AttributeCombValueStats, - value: &Value, - ) -> f64 { - // because distr does not include the values in MCVs, we need to compute the CDFs there as - // well because nulls return false in any comparison, they are never included when - // computing range selectivity - let distr_leq_freq = per_attribute_stats.distr.as_ref().unwrap().cdf(value); - let value = value.clone(); - let pred = Box::new(move |val: &AttributeCombValue| *val[0].as_ref().unwrap() <= value); - let mcvs_leq_freq = per_attribute_stats.mcvs.freq_over_pred(pred); - let ret_freq = distr_leq_freq + mcvs_leq_freq; - assert!( - (0.0..=1.0).contains(&ret_freq), - "ret_freq ({}) should be in [0, 1]", - ret_freq - ); - ret_freq - } - - /// Compute the frequency of values in a attribute less than the given value. - /// - /// Currently, we only support calculating the equality selectivity for an existed attribute, - /// not a derived attribute. - /// TODO: Support derived attributes. - async fn get_attribute_lt_value_freq( - &self, - attribute_stats: &AttributeCombValueStats, - table_id: TableId, - attr_base_index: u64, - value: &Value, - ) -> CostModelResult { - // depending on whether value is in mcvs or not, we use different logic to turn total_lt_cdf - // into total_leq_cdf this logic just so happens to be the exact same logic as - // get_attribute_equality_selectivity implements - let ret_freq = Self::get_attribute_leq_value_freq(attribute_stats, value) - - self - .get_attribute_equality_selectivity(table_id, attr_base_index, value, true) - .await?; - assert!( - (0.0..=1.0).contains(&ret_freq), - "ret_freq ({}) should be in [0, 1]", - ret_freq - ); - Ok(ret_freq) - } - - /// Get the selectivity of an expression of the form "attribute =/> value" (or "value - /// =/> attribute"). Computes selectivity based off of statistics. - /// Range predicates are handled entirely differently from equality predicates so this is its - /// own function. If it is unable to find the statistics, it returns DEFAULT_INEQ_SEL. - /// The selectivity is computed as quantile of the right bound minus quantile of the left bound. - /// - /// Currently, we only support calculating the equality selectivity for an existed attribute, - /// not a derived attribute. - /// TODO: Support derived attributes. - pub(crate) async fn get_attribute_range_selectivity( - &self, - table_id: TableId, - attr_base_index: u64, - start: Bound<&Value>, - end: Bound<&Value>, - ) -> CostModelResult { - // TODO: Consider attribute is a derived attribute - if let Some(attribute_stats) = self - .get_attribute_comb_stats(table_id, &[attr_base_index]) - .await? - { - let left_quantile = match start { - Bound::Unbounded => 0.0, - Bound::Included(value) => { - self.get_attribute_lt_value_freq( - &attribute_stats, - table_id, - attr_base_index, - value, - ) - .await? - } - Bound::Excluded(value) => { - Self::get_attribute_leq_value_freq(&attribute_stats, value) - } - }; - let right_quantile = match end { - Bound::Unbounded => 1.0, - Bound::Included(value) => { - Self::get_attribute_leq_value_freq(&attribute_stats, value) - } - Bound::Excluded(value) => { - self.get_attribute_lt_value_freq( - &attribute_stats, - table_id, - attr_base_index, - value, - ) - .await? - } - }; - assert!( - left_quantile <= right_quantile, - "left_quantile ({}) should be <= right_quantile ({})", - left_quantile, - right_quantile - ); - Ok(right_quantile - left_quantile) - } else { - Ok(DEFAULT_INEQ_SEL) - } - } -} diff --git a/optd-cost-model/src/cost/filter/comp_op.rs b/optd-cost-model/src/cost/filter/comp_op.rs deleted file mode 100644 index 526c901..0000000 --- a/optd-cost-model/src/cost/filter/comp_op.rs +++ /dev/null @@ -1,280 +0,0 @@ -use std::ops::Bound; - -use crate::{ - common::{ - nodes::{ArcPredicateNode, PredicateType, ReprPredicateNode}, - predicates::{ - attr_index_pred::AttrIndexPred, bin_op_pred::BinOpType, cast_pred::CastPred, - constant_pred::ConstantPred, - }, - properties::attr_ref::{AttrRef, BaseTableAttrRef}, - types::GroupId, - values::Value, - }, - cost_model::CostModelImpl, - stats::{DEFAULT_EQ_SEL, DEFAULT_INEQ_SEL, UNIMPLEMENTED_SEL}, - storage::CostModelStorageManager, - CostModelResult, -}; - -impl CostModelImpl { - /// Comparison operators are the base case for recursion in get_filter_selectivity() - pub(crate) async fn get_comp_op_selectivity( - &self, - group_id: GroupId, - comp_bin_op_typ: BinOpType, - left: ArcPredicateNode, - right: ArcPredicateNode, - ) -> CostModelResult { - assert!(comp_bin_op_typ.is_comparison()); - - // I intentionally performed moves on left and right. This way, we don't accidentally use - // them after this block - let semantic_res = self.get_semantic_nodes(group_id, left, right).await; - if semantic_res.is_err() { - return Ok(Self::get_default_comparison_op_selectivity(comp_bin_op_typ)); - } - let (attr_ref_exprs, values, non_attr_ref_exprs, is_left_attr_ref) = semantic_res.unwrap(); - - // Handle the different cases of semantic nodes. - if attr_ref_exprs.is_empty() { - Ok(UNIMPLEMENTED_SEL) - } else if attr_ref_exprs.len() == 1 { - let attr_ref_expr = attr_ref_exprs - .first() - .expect("we just checked that attr_ref_exprs.len() == 1"); - let attr_ref_idx = attr_ref_expr.attr_index(); - - if let AttrRef::BaseTableAttrRef(BaseTableAttrRef { table_id, attr_idx }) = - self.memo.get_attribute_ref(group_id, attr_ref_idx) - { - if values.len() == 1 { - let value = values - .first() - .expect("we just checked that values.len() == 1"); - match comp_bin_op_typ { - BinOpType::Eq => { - self.get_attribute_equality_selectivity(table_id, attr_idx, value, true) - .await - } - BinOpType::Neq => { - self.get_attribute_equality_selectivity( - table_id, - attr_ref_idx, - value, - false, - ) - .await - } - BinOpType::Lt | BinOpType::Leq | BinOpType::Gt | BinOpType::Geq => { - let start = match (comp_bin_op_typ, is_left_attr_ref) { - (BinOpType::Lt, true) | (BinOpType::Geq, false) => Bound::Unbounded, - (BinOpType::Leq, true) | (BinOpType::Gt, false) => Bound::Unbounded, - (BinOpType::Gt, true) | (BinOpType::Leq, false) => Bound::Excluded(value), - (BinOpType::Geq, true) | (BinOpType::Lt, false) => Bound::Included(value), - _ => unreachable!("all comparison BinOpTypes were enumerated. this should be unreachable"), - }; - let end = match (comp_bin_op_typ, is_left_attr_ref) { - (BinOpType::Lt, true) | (BinOpType::Geq, false) => Bound::Excluded(value), - (BinOpType::Leq, true) | (BinOpType::Gt, false) => Bound::Included(value), - (BinOpType::Gt, true) | (BinOpType::Leq, false) => Bound::Unbounded, - (BinOpType::Geq, true) | (BinOpType::Lt, false) => Bound::Unbounded, - _ => unreachable!("all comparison BinOpTypes were enumerated. this should be unreachable"), - }; - self.get_attribute_range_selectivity(table_id, attr_ref_idx, start, end) - .await - } - _ => unreachable!( - "all comparison BinOpTypes were enumerated. this should be unreachable" - ), - } - } else { - let non_attr_ref_expr = non_attr_ref_exprs.first().expect( - "non_attr_ref_exprs should have a value since attr_ref_exprs.len() == 1", - ); - - match non_attr_ref_expr.as_ref().typ { - PredicateType::BinOp(_) => { - Ok(Self::get_default_comparison_op_selectivity(comp_bin_op_typ)) - } - PredicateType::Cast => Ok(UNIMPLEMENTED_SEL), - PredicateType::Constant(_) => { - unreachable!( - "we should have handled this in the values.len() == 1 branch" - ) - } - _ => unimplemented!( - "unhandled case of comparing a attribute ref node to {}", - non_attr_ref_expr.as_ref().typ - ), - } - } - } else { - // TODO: attribute is derived - Ok(Self::get_default_comparison_op_selectivity(comp_bin_op_typ)) - } - } else if attr_ref_exprs.len() == 2 { - Ok(Self::get_default_comparison_op_selectivity(comp_bin_op_typ)) - } else { - unreachable!("we could have at most pushed left and right into attr_ref_exprs") - } - } - - /// Convert the left and right child nodes of some operation to what they semantically are. - /// This is convenient to avoid repeating the same logic just with "left" and "right" swapped. - /// The last return value is true when the input node (left) is a AttributeRefPred. - #[allow(clippy::type_complexity)] - async fn get_semantic_nodes( - &self, - group_id: GroupId, - left: ArcPredicateNode, - right: ArcPredicateNode, - ) -> CostModelResult<(Vec, Vec, Vec, bool)> { - let mut attr_ref_exprs = vec![]; - let mut values = vec![]; - let mut non_attr_ref_exprs = vec![]; - let is_left_attr_ref; - - // Recursively unwrap casts as much as we can. - let mut uncasted_left = left; - let mut uncasted_right = right; - loop { - // println!("loop {}, uncasted_left={:?}, uncasted_right={:?}", Local::now(), - // uncasted_left, uncasted_right); - if uncasted_left.as_ref().typ == PredicateType::Cast - && uncasted_right.as_ref().typ == PredicateType::Cast - { - let left_cast_expr = CastPred::from_pred_node(uncasted_left) - .expect("we already checked that the type is Cast"); - let right_cast_expr = CastPred::from_pred_node(uncasted_right) - .expect("we already checked that the type is Cast"); - assert!(left_cast_expr.cast_to() == right_cast_expr.cast_to()); - uncasted_left = left_cast_expr.child().into_pred_node(); - uncasted_right = right_cast_expr.child().into_pred_node(); - } else if uncasted_left.as_ref().typ == PredicateType::Cast - || uncasted_right.as_ref().typ == PredicateType::Cast - { - let is_left_cast = uncasted_left.as_ref().typ == PredicateType::Cast; - let (mut cast_node, mut non_cast_node) = if is_left_cast { - (uncasted_left, uncasted_right) - } else { - (uncasted_right, uncasted_left) - }; - - let cast_expr = CastPred::from_pred_node(cast_node) - .expect("we already checked that the type is Cast"); - let cast_expr_child = cast_expr.child().into_pred_node(); - let cast_expr_cast_to = cast_expr.cast_to(); - - let should_break = match cast_expr_child.typ { - PredicateType::Constant(_) => { - cast_node = ConstantPred::new( - ConstantPred::from_pred_node(cast_expr_child) - .expect("we already checked that the type is Constant") - .value() - .convert_to_type(cast_expr_cast_to), - ) - .into_pred_node(); - false - } - PredicateType::AttrIndex => { - let attr_ref_expr = AttrIndexPred::from_pred_node(cast_expr_child) - .expect("we already checked that the type is AttributeRef"); - let attr_ref_idx = attr_ref_expr.attr_index(); - cast_node = attr_ref_expr.into_pred_node(); - // The "invert" cast is to invert the cast so that we're casting the - // non_cast_node to the attribute's original type. - let attribute_info = self.memo.get_attribute_info(group_id, attr_ref_idx); - let invert_cast_data_type = &attribute_info.typ.into_data_type(); - - match non_cast_node.typ { - PredicateType::AttrIndex => { - // In general, there's no way to remove the Cast here. We can't move - // the Cast to the other AttributeRef - // because that would lead to an infinite loop. Thus, we just leave - // the cast where it is and break. - true - } - _ => { - non_cast_node = - CastPred::new(non_cast_node, invert_cast_data_type.clone()) - .into_pred_node(); - false - } - } - } - _ => todo!(), - }; - - (uncasted_left, uncasted_right) = if is_left_cast { - (cast_node, non_cast_node) - } else { - (non_cast_node, cast_node) - }; - - if should_break { - break; - } - } else { - break; - } - } - - // Sort nodes into attr_ref_exprs, values, and non_attr_ref_exprs - match uncasted_left.as_ref().typ { - PredicateType::AttrIndex => { - is_left_attr_ref = true; - attr_ref_exprs.push( - AttrIndexPred::from_pred_node(uncasted_left) - .expect("we already checked that the type is AttributeRef"), - ); - } - PredicateType::Constant(_) => { - is_left_attr_ref = false; - values.push( - ConstantPred::from_pred_node(uncasted_left) - .expect("we already checked that the type is Constant") - .value(), - ) - } - _ => { - is_left_attr_ref = false; - non_attr_ref_exprs.push(uncasted_left); - } - } - match uncasted_right.as_ref().typ { - PredicateType::AttrIndex => { - attr_ref_exprs.push( - AttrIndexPred::from_pred_node(uncasted_right) - .expect("we already checked that the type is AttributeRef"), - ); - } - PredicateType::Constant(_) => values.push( - ConstantPred::from_pred_node(uncasted_right) - .expect("we already checked that the type is Constant") - .value(), - ), - _ => { - non_attr_ref_exprs.push(uncasted_right); - } - } - - assert!(attr_ref_exprs.len() + values.len() + non_attr_ref_exprs.len() == 2); - Ok((attr_ref_exprs, values, non_attr_ref_exprs, is_left_attr_ref)) - } - - /// The default selectivity of a comparison expression - /// Used when one side of the comparison is a attribute while the other side is something too - /// complex/impossible to evaluate (subquery, UDF, another attribute, we have no stats, etc.) - fn get_default_comparison_op_selectivity(comp_bin_op_typ: BinOpType) -> f64 { - assert!(comp_bin_op_typ.is_comparison()); - match comp_bin_op_typ { - BinOpType::Eq => DEFAULT_EQ_SEL, - BinOpType::Neq => 1.0 - DEFAULT_EQ_SEL, - BinOpType::Lt | BinOpType::Leq | BinOpType::Gt | BinOpType::Geq => DEFAULT_INEQ_SEL, - _ => unreachable!( - "all comparison BinOpTypes were enumerated. this should be unreachable" - ), - } - } -} diff --git a/optd-cost-model/src/cost/filter/constant.rs b/optd-cost-model/src/cost/filter/constant.rs deleted file mode 100644 index e131bde..0000000 --- a/optd-cost-model/src/cost/filter/constant.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::{ - common::{ - nodes::{ArcPredicateNode, PredicateType}, - predicates::constant_pred::ConstantType, - values::Value, - }, - cost_model::CostModelImpl, - storage::CostModelStorageManager, -}; - -impl CostModelImpl { - pub(crate) fn get_constant_selectivity(const_node: ArcPredicateNode) -> f64 { - if let PredicateType::Constant(const_typ) = const_node.typ { - if matches!(const_typ, ConstantType::Bool) { - let value = const_node - .as_ref() - .data - .as_ref() - .expect("constants should have data"); - if let Value::Bool(bool_value) = value { - if *bool_value { - 1.0 - } else { - 0.0 - } - } else { - unreachable!( - "if the typ is ConstantType::Bool, the value should be a Value::Bool" - ) - } - } else { - panic!("selectivity is not defined on constants which are not bools") - } - } else { - panic!("get_constant_selectivity must be called on a constant") - } - } -} diff --git a/optd-cost-model/src/cost/filter/core.rs b/optd-cost-model/src/cost/filter/core.rs deleted file mode 100644 index 44651ed..0000000 --- a/optd-cost-model/src/cost/filter/core.rs +++ /dev/null @@ -1,872 +0,0 @@ -use crate::{ - common::{ - nodes::{ArcPredicateNode, PredicateType, ReprPredicateNode}, - predicates::{in_list_pred::InListPred, like_pred::LikePred, un_op_pred::UnOpType}, - types::GroupId, - }, - cost_model::CostModelImpl, - stats::UNIMPLEMENTED_SEL, - storage::CostModelStorageManager, - CostModelResult, EstimatedStatistic, -}; - -impl CostModelImpl { - // TODO: is it a good design to pass table_id here? I think it needs to be refactored. - // Consider to remove table_id. - pub async fn get_filter_row_cnt( - &self, - child_row_cnt: EstimatedStatistic, - group_id: GroupId, - cond: ArcPredicateNode, - ) -> CostModelResult { - let selectivity = { self.get_filter_selectivity(group_id, cond).await? }; - Ok(EstimatedStatistic((child_row_cnt.0 * selectivity).max(1.0))) - } - - pub async fn get_filter_selectivity( - &self, - group_id: GroupId, - expr_tree: ArcPredicateNode, - ) -> CostModelResult { - Box::pin(async move { - match &expr_tree.typ { - PredicateType::Constant(_) => Ok(Self::get_constant_selectivity(expr_tree)), - PredicateType::AttrIndex => unimplemented!("check bool type or else panic"), - PredicateType::UnOp(un_op_typ) => { - assert!(expr_tree.children.len() == 1); - let child = expr_tree.child(0); - match un_op_typ { - // not doesn't care about nulls so there's no complex logic. it just reverses - // the selectivity for instance, != _will not_ include nulls - // but "NOT ==" _will_ include nulls - UnOpType::Not => Ok(1.0 - self.get_filter_selectivity(group_id, child).await?), - UnOpType::Neg => panic!( - "the selectivity of operations that return numerical values is undefined" - ), - } - } - PredicateType::BinOp(bin_op_typ) => { - assert!(expr_tree.children.len() == 2); - let left_child = expr_tree.child(0); - let right_child = expr_tree.child(1); - - if bin_op_typ.is_comparison() { - self.get_comp_op_selectivity(group_id, *bin_op_typ, left_child, right_child).await - } else if bin_op_typ.is_numerical() { - panic!( - "the selectivity of operations that return numerical values is undefined" - ) - } else { - unreachable!("all BinOpTypes should be true for at least one is_*() function") - } - } - PredicateType::LogOp(log_op_typ) => { - self.get_log_op_selectivity(group_id, *log_op_typ, &expr_tree.children).await - } - PredicateType::Func(_) => unimplemented!("check bool type or else panic"), - PredicateType::SortOrder(_) => { - panic!("the selectivity of sort order expressions is undefined") - } - PredicateType::Between => Ok(UNIMPLEMENTED_SEL), - PredicateType::Cast => unimplemented!("check bool type or else panic"), - PredicateType::Like => { - let like_expr = LikePred::from_pred_node(expr_tree).unwrap(); - self.get_like_selectivity(group_id, &like_expr).await - } - PredicateType::DataType(_) => { - panic!("the selectivity of a data type is not defined") - } - PredicateType::InList => { - let in_list_expr = InListPred::from_pred_node(expr_tree).unwrap(); - self.get_in_list_selectivity(group_id, &in_list_expr).await - } - _ => unreachable!( - "all expression DfPredType were enumerated. this should be unreachable" - ), - } - }).await - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use crate::{ - common::{ - predicates::{ - bin_op_pred::BinOpType, constant_pred::ConstantType, log_op_pred::LogOpType, - un_op_pred::UnOpType, - }, - types::TableId, - values::Value, - }, - stats::{utilities::simple_map::SimpleMap, Distribution, MostCommonValues, DEFAULT_EQ_SEL}, - test_utils::tests::*, - }; - use arrow_schema::DataType; - - #[tokio::test] - async fn test_const() { - let cost_model = create_mock_cost_model( - vec![TableId(0)], - vec![HashMap::from([(0, empty_per_attr_stats())])], - vec![None], - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, cnst(Value::Bool(true))) - .await - .unwrap(), - 1.0 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, cnst(Value::Bool(false))) - .await - .unwrap(), - 0.0 - ); - } - - #[tokio::test] - async fn test_attr_ref_eq_constint_in_mcv() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::new(vec![( - vec![Some(Value::Int32(1))], - 0.3, - )])), - None, - 0, - 0.0, - ); - let table_id = TableId(0); - let cost_model = create_mock_cost_model( - vec![table_id], - vec![HashMap::from([(0, per_attribute_stats)])], - vec![None], - ); - - let expr_tree = bin_op( - BinOpType::Eq, - attr_index(0), // TODO: Fix this - cnst(Value::Int32(1)), - ); - let expr_tree_rev = bin_op( - BinOpType::Eq, - cnst(Value::Int32(1)), - attr_index(0), // TODO: Fix this - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree) - .await - .unwrap(), - 0.3 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_rev) - .await - .unwrap(), - 0.3 - ); - } - - #[tokio::test] - async fn test_attr_ref_eq_constint_not_in_mcv() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::new(vec![ - (vec![Some(Value::Int32(1))], 0.2), - (vec![Some(Value::Int32(3))], 0.44), - ])), - None, - 5, - 0.0, - ); - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![None], - ); - - let expr_tree = bin_op(BinOpType::Eq, attr_index(0), cnst(Value::Int32(2))); - let expr_tree_rev = bin_op(BinOpType::Eq, cnst(Value::Int32(2)), attr_index(0)); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree) - .await - .unwrap(), - 0.12 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_rev) - .await - .unwrap(), - 0.12 - ); - } - - /// I only have one test for NEQ since I'll assume that it uses the same underlying logic as EQ - #[tokio::test] - async fn test_attr_ref_neq_constint_in_mcv() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::new(vec![( - vec![Some(Value::Int32(1))], - 0.3, - )])), - None, - 0, - 0.0, - ); - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![None], - ); - - let expr_tree = bin_op(BinOpType::Neq, attr_index(0), cnst(Value::Int32(1))); - let expr_tree_rev = bin_op(BinOpType::Neq, cnst(Value::Int32(1)), attr_index(0)); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree) - .await - .unwrap(), - 1.0 - 0.3 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_rev) - .await - .unwrap(), - 1.0 - 0.3 - ); - } - - #[tokio::test] - async fn test_attr_ref_leq_constint_no_mcvs_in_range() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::default()), - Some(Distribution::SimpleDistribution(SimpleMap::new(vec![( - Value::Int32(15), - 0.7, - )]))), - 10, - 0.0, - ); - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![None], - ); - - let expr_tree = bin_op(BinOpType::Leq, attr_index(0), cnst(Value::Int32(15))); - let expr_tree_rev = bin_op(BinOpType::Gt, cnst(Value::Int32(15)), attr_index(0)); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree) - .await - .unwrap(), - 0.7 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_rev) - .await - .unwrap(), - 0.7 - ); - } - - #[tokio::test] - async fn test_attr_ref_leq_constint_with_mcvs_in_range_not_at_border() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::new(vec![ - (vec![Some(Value::Int32(6))], 0.05), - (vec![Some(Value::Int32(10))], 0.1), - (vec![Some(Value::Int32(17))], 0.08), - (vec![Some(Value::Int32(25))], 0.07), - ])), - Some(Distribution::SimpleDistribution(SimpleMap::new(vec![( - Value::Int32(15), - 0.7, - )]))), - 10, - 0.0, - ); - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![None], - ); - - let expr_tree = bin_op(BinOpType::Leq, attr_index(0), cnst(Value::Int32(15))); - let expr_tree_rev = bin_op(BinOpType::Gt, cnst(Value::Int32(15)), attr_index(0)); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree) - .await - .unwrap(), - 0.85 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_rev) - .await - .unwrap(), - 0.85 - ); - } - - #[tokio::test] - async fn test_attr_ref_leq_constint_with_mcv_at_border() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::new(vec![ - (vec![Some(Value::Int32(6))], 0.05), - (vec![Some(Value::Int32(10))], 0.1), - (vec![Some(Value::Int32(15))], 0.08), - (vec![Some(Value::Int32(25))], 0.07), - ])), - Some(Distribution::SimpleDistribution(SimpleMap::new(vec![( - Value::Int32(15), - 0.7, - )]))), - 10, - 0.0, - ); - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![None], - ); - - let expr_tree = bin_op(BinOpType::Leq, attr_index(0), cnst(Value::Int32(15))); - let expr_tree_rev = bin_op(BinOpType::Gt, cnst(Value::Int32(15)), attr_index(0)); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree) - .await - .unwrap(), - 0.93 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_rev) - .await - .unwrap(), - 0.93 - ); - } - - #[tokio::test] - async fn test_attr_ref_lt_constint_no_mcvs_in_range() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::default()), - Some(Distribution::SimpleDistribution(SimpleMap::new(vec![( - Value::Int32(15), - 0.7, - )]))), - 10, - 0.0, - ); - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![None], - ); - - let expr_tree = bin_op(BinOpType::Lt, attr_index(0), cnst(Value::Int32(15))); - let expr_tree_rev = bin_op(BinOpType::Geq, cnst(Value::Int32(15)), attr_index(0)); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree) - .await - .unwrap(), - 0.6 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_rev) - .await - .unwrap(), - 0.6 - ); - } - - #[tokio::test] - async fn test_attr_ef_lt_constint_with_mcvs_in_range_not_at_border() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::new(vec![ - (vec![Some(Value::Int32(6))], 0.05), - (vec![Some(Value::Int32(10))], 0.1), - (vec![Some(Value::Int32(17))], 0.08), - (vec![Some(Value::Int32(25))], 0.07), - ])), - Some(Distribution::SimpleDistribution(SimpleMap::new(vec![( - Value::Int32(15), - 0.7, - )]))), - 11, /* there are 4 MCVs which together add up to 0.3. With 11 total ndistinct, each - * remaining value has freq 0.1 */ - 0.0, - ); - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![None], - ); - - let expr_tree = bin_op(BinOpType::Lt, attr_index(0), cnst(Value::Int32(15))); - let expr_tree_rev = bin_op(BinOpType::Geq, cnst(Value::Int32(15)), attr_index(0)); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree) - .await - .unwrap(), - 0.75 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_rev) - .await - .unwrap(), - 0.75 - ); - } - - #[tokio::test] - async fn test_attr_ref_lt_constint_with_mcv_at_border() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::new(vec![ - (vec![Some(Value::Int32(6))], 0.05), - (vec![Some(Value::Int32(10))], 0.1), - (vec![Some(Value::Int32(15))], 0.08), - (vec![Some(Value::Int32(25))], 0.07), - ])), - Some(Distribution::SimpleDistribution(SimpleMap::new(vec![( - Value::Int32(15), - 0.7, - )]))), - 11, /* there are 4 MCVs which together add up to 0.3. With 11 total ndistinct, each - * remaining value has freq 0.1 */ - 0.0, - ); - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![None], - ); - - let expr_tree = bin_op(BinOpType::Lt, attr_index(0), cnst(Value::Int32(15))); - let expr_tree_rev = bin_op(BinOpType::Geq, cnst(Value::Int32(15)), attr_index(0)); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree) - .await - .unwrap(), - 0.85 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_rev) - .await - .unwrap(), - 0.85 - ); - } - - /// I have fewer tests for GT since I'll assume that it uses the same underlying logic as LEQ - /// The only interesting thing to test is that if there are nulls, those aren't included in GT - #[tokio::test] - async fn test_attr_ref_gt_constint() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::default()), - Some(Distribution::SimpleDistribution(SimpleMap::new(vec![( - Value::Int32(15), - 0.7, - )]))), - 10, - 0.0, - ); - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![None], - ); - - let expr_tree = bin_op(BinOpType::Gt, attr_index(0), cnst(Value::Int32(15))); - let expr_tree_rev = bin_op(BinOpType::Leq, cnst(Value::Int32(15)), attr_index(0)); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree) - .await - .unwrap(), - 1.0 - 0.7 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_rev) - .await - .unwrap(), - 1.0 - 0.7 - ); - } - - #[tokio::test] - async fn test_attr_ref_geq_constint() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::default()), - Some(Distribution::SimpleDistribution(SimpleMap::new(vec![( - Value::Int32(15), - 0.7, - )]))), - 10, - 0.0, - ); - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![None], - ); - - let expr_tree = bin_op(BinOpType::Geq, attr_index(0), cnst(Value::Int32(15))); - let expr_tree_rev = bin_op(BinOpType::Lt, cnst(Value::Int32(15)), attr_index(0)); - - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree) - .await - .unwrap(), - 1.0 - 0.6 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_rev) - .await - .unwrap(), - 1.0 - 0.6 - ); - } - - #[tokio::test] - async fn test_and() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::new(vec![ - (vec![Some(Value::Int32(1))], 0.3), - (vec![Some(Value::Int32(5))], 0.5), - (vec![Some(Value::Int32(8))], 0.2), - ])), - None, - 0, - 0.0, - ); - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![None], - ); - - let eq1 = bin_op(BinOpType::Eq, attr_index(0), cnst(Value::Int32(1))); - let eq5 = bin_op(BinOpType::Eq, attr_index(0), cnst(Value::Int32(5))); - let eq8 = bin_op(BinOpType::Eq, attr_index(0), cnst(Value::Int32(8))); - let expr_tree = log_op(LogOpType::And, vec![eq1.clone(), eq5.clone(), eq8.clone()]); - let expr_tree_shift1 = log_op(LogOpType::And, vec![eq5.clone(), eq8.clone(), eq1.clone()]); - let expr_tree_shift2 = log_op(LogOpType::And, vec![eq8.clone(), eq1.clone(), eq5.clone()]); - - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree) - .await - .unwrap(), - 0.03 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_shift1) - .await - .unwrap(), - 0.03 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_shift2) - .await - .unwrap(), - 0.03 - ); - } - - #[tokio::test] - async fn test_or() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::new(vec![ - (vec![Some(Value::Int32(1))], 0.3), - (vec![Some(Value::Int32(5))], 0.5), - (vec![Some(Value::Int32(8))], 0.2), - ])), - None, - 0, - 0.0, - ); - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![None], - ); - - let eq1 = bin_op(BinOpType::Eq, attr_index(0), cnst(Value::Int32(1))); - let eq5 = bin_op(BinOpType::Eq, attr_index(0), cnst(Value::Int32(5))); - let eq8 = bin_op(BinOpType::Eq, attr_index(0), cnst(Value::Int32(8))); - let expr_tree = log_op(LogOpType::Or, vec![eq1.clone(), eq5.clone(), eq8.clone()]); - let expr_tree_shift1 = log_op(LogOpType::Or, vec![eq5.clone(), eq8.clone(), eq1.clone()]); - let expr_tree_shift2 = log_op(LogOpType::Or, vec![eq8.clone(), eq1.clone(), eq5.clone()]); - - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree) - .await - .unwrap(), - 0.72 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_shift1) - .await - .unwrap(), - 0.72 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_shift2) - .await - .unwrap(), - 0.72 - ); - } - - #[tokio::test] - async fn test_not() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::new(vec![( - vec![Some(Value::Int32(1))], - 0.3, - )])), - None, - 0, - 0.0, - ); - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![None], - ); - - let expr_tree = un_op( - UnOpType::Not, - bin_op(BinOpType::Eq, attr_index(0), cnst(Value::Int32(1))), - ); - - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree) - .await - .unwrap(), - 0.7 - ); - } - - // I didn't test any non-unique cases with filter. The non-unique tests without filter should - // cover that - - #[tokio::test] - async fn test_attr_ref_eq_cast_value() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::new(vec![( - vec![Some(Value::Int32(1))], - 0.3, - )])), - None, - 0, - 0.0, - ); - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![None], - ); - - let expr_tree = bin_op( - BinOpType::Eq, - attr_index(0), - cast(cnst(Value::Int64(1)), DataType::Int32), - ); - let expr_tree_rev = bin_op( - BinOpType::Eq, - cast(cnst(Value::Int64(1)), DataType::Int32), - attr_index(0), - ); - - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree) - .await - .unwrap(), - 0.3 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_rev) - .await - .unwrap(), - 0.3 - ); - } - - #[tokio::test] - async fn test_cast_attr_ref_eq_value() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::new(vec![( - vec![Some(Value::Int32(1))], - 0.3, - )])), - None, - 0, - 0.1, - ); - let cost_model = create_mock_cost_model_with_attr_types( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - ConstantType::Int32, - )])], - vec![None], - ); - - let expr_tree = bin_op( - BinOpType::Eq, - cast(attr_index(0), DataType::Int64), // TODO: Fix this - cnst(Value::Int64(1)), - ); - let expr_tree_rev = bin_op( - BinOpType::Eq, - cnst(Value::Int64(1)), - cast(attr_index(0), DataType::Int64), // TODO: Fix this - ); - - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree) - .await - .unwrap(), - 0.3 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_rev) - .await - .unwrap(), - 0.3 - ); - } - - /// In this case, we should leave the Cast as is. - /// - /// Note that the test only checks the selectivity and thus doesn't explicitly test that the - /// Cast is indeed left as is. However, if get_filter_selectivity() doesn't crash, that's a - /// pretty good signal that the Cast was left as is. - #[tokio::test] - async fn test_cast_attr_ref_eq_attr_ref() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::default()), - None, - 0, - 0.0, - ); - - let cost_model = create_mock_cost_model_with_attr_types( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![HashMap::from([ - (TEST_ATTR1_BASE_INDEX, ConstantType::Int32), - (TEST_ATTR2_BASE_INDEX, ConstantType::Int64), - ])], - vec![None], - ); - - let expr_tree = bin_op( - BinOpType::Eq, - cast(attr_index(0), DataType::Int64), - attr_index(1), - ); - let expr_tree_rev = bin_op( - BinOpType::Eq, - attr_index(1), - cast(attr_index(0), DataType::Int64), - ); - - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree) - .await - .unwrap(), - DEFAULT_EQ_SEL - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_filter_selectivity(TEST_GROUP1_ID, expr_tree_rev) - .await - .unwrap(), - DEFAULT_EQ_SEL - ); - } -} diff --git a/optd-cost-model/src/cost/filter/in_list.rs b/optd-cost-model/src/cost/filter/in_list.rs deleted file mode 100644 index d8838a8..0000000 --- a/optd-cost-model/src/cost/filter/in_list.rs +++ /dev/null @@ -1,160 +0,0 @@ -use crate::{ - common::{ - nodes::{PredicateType, ReprPredicateNode}, - predicates::{ - attr_index_pred::AttrIndexPred, constant_pred::ConstantPred, in_list_pred::InListPred, - }, - properties::attr_ref::{AttrRef, BaseTableAttrRef}, - types::GroupId, - }, - cost_model::CostModelImpl, - stats::UNIMPLEMENTED_SEL, - storage::CostModelStorageManager, - CostModelResult, -}; - -impl CostModelImpl { - /// Only support attrA in (val1, val2, val3) where attrA is a attribute ref and - /// val1, val2, val3 are constants. - pub(crate) async fn get_in_list_selectivity( - &self, - group_id: GroupId, - expr: &InListPred, - ) -> CostModelResult { - let child = expr.child(); - - // Check child is a attribute ref. - if !matches!(child.typ, PredicateType::AttrIndex) { - return Ok(UNIMPLEMENTED_SEL); - } - - // Check all expressions in the list are constants. - let list_exprs = expr.list().to_vec(); - if list_exprs - .iter() - .any(|expr| !matches!(expr.typ, PredicateType::Constant(_))) - { - return Ok(UNIMPLEMENTED_SEL); - } - - // Convert child and const expressions to concrete types. - let attr_ref_pred = AttrIndexPred::from_pred_node(child).unwrap(); - let attr_ref_idx = attr_ref_pred.attr_index(); - - let list_exprs = list_exprs - .into_iter() - .map(|expr| { - ConstantPred::from_pred_node(expr) - .expect("we already checked all list elements are constants") - }) - .collect::>(); - let negated = expr.negated(); - - if let AttrRef::BaseTableAttrRef(BaseTableAttrRef { table_id, attr_idx }) = - self.memo.get_attribute_ref(group_id, attr_ref_idx) - { - let mut in_sel = 0.0; - for expr in &list_exprs { - let selectivity = self - .get_attribute_equality_selectivity( - table_id, - attr_idx, - &expr.value(), - /* is_equality */ true, - ) - .await?; - in_sel += selectivity; - } - in_sel = in_sel.min(1.0); - if negated { - Ok(1.0 - in_sel) - } else { - Ok(in_sel) - } - } else { - // TODO: Child is a derived attribute. - Ok(UNIMPLEMENTED_SEL) - } - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use crate::{ - common::values::Value, - stats::{utilities::simple_map::SimpleMap, MostCommonValues}, - test_utils::tests::*, - }; - - #[tokio::test] - async fn test_in_list() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::new(vec![ - (vec![Some(Value::Int32(1))], 0.8), - (vec![Some(Value::Int32(2))], 0.2), - ])), - None, - 2, - 0.0, - ); - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![None], - ); - - assert_approx_eq::assert_approx_eq!( - cost_model - .get_in_list_selectivity(TEST_GROUP1_ID, &in_list(0, vec![Value::Int32(1)], false)) - .await - .unwrap(), - 0.8 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_in_list_selectivity( - TEST_GROUP1_ID, - &in_list(0, vec![Value::Int32(1), Value::Int32(2)], false) - ) - .await - .unwrap(), - 1.0 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_in_list_selectivity(TEST_GROUP1_ID, &in_list(0, vec![Value::Int32(3)], false)) - .await - .unwrap(), - 0.0 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_in_list_selectivity(TEST_GROUP1_ID, &in_list(0, vec![Value::Int32(1)], true)) - .await - .unwrap(), - 0.2 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_in_list_selectivity( - TEST_GROUP1_ID, - &in_list(0, vec![Value::Int32(1), Value::Int32(2)], true) - ) - .await - .unwrap(), - 0.0 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_in_list_selectivity(TEST_GROUP1_ID, &in_list(0, vec![Value::Int32(3)], true)) // TODO: Fix this - .await - .unwrap(), - 1.0 - ); - } -} diff --git a/optd-cost-model/src/cost/filter/like.rs b/optd-cost-model/src/cost/filter/like.rs deleted file mode 100644 index ff56f44..0000000 --- a/optd-cost-model/src/cost/filter/like.rs +++ /dev/null @@ -1,207 +0,0 @@ -use datafusion::arrow::{array::StringArray, compute::like}; - -use crate::{ - common::{ - nodes::{PredicateType, ReprPredicateNode}, - predicates::{ - attr_index_pred::AttrIndexPred, constant_pred::ConstantPred, like_pred::LikePred, - }, - properties::attr_ref::{AttrRef, BaseTableAttrRef}, - types::GroupId, - }, - cost_model::CostModelImpl, - stats::{ - AttributeCombValue, FIXED_CHAR_SEL_FACTOR, FULL_WILDCARD_SEL_FACTOR, UNIMPLEMENTED_SEL, - }, - storage::CostModelStorageManager, - CostModelResult, -}; - -impl CostModelImpl { - /// Compute the selectivity of a (NOT) LIKE expression. - /// - /// The logic is somewhat similar to Postgres but different. Postgres first estimates the - /// histogram part of the population and then add up data for any MCV values. If the - /// histogram is large enough, it just uses the number of matches in the histogram, - /// otherwise it estimates the fixed prefix and remainder of pattern separately and - /// combine them. - /// - /// Our approach is simpler and less selective. Firstly, we don't use histogram. The selectivity - /// is composed of MCV frequency and non-MCV selectivity. MCV frequency is computed by - /// adding up frequencies of MCVs that match the pattern. Non-MCV selectivity is computed - /// in the same way that Postgres computes selectivity for the wildcard part of the pattern. - pub(crate) async fn get_like_selectivity( - &self, - group_id: GroupId, - like_expr: &LikePred, - ) -> CostModelResult { - let child = like_expr.child(); - - // Check child is a attribute ref. - if !matches!(child.typ, PredicateType::AttrIndex) { - return Ok(UNIMPLEMENTED_SEL); - } - - // Check pattern is a constant. - let pattern = like_expr.pattern(); - if !matches!(pattern.typ, PredicateType::Constant(_)) { - return Ok(UNIMPLEMENTED_SEL); - } - - let attr_ref_pred = AttrIndexPred::from_pred_node(child).unwrap(); - let attr_ref_idx = attr_ref_pred.attr_index(); - - if let AttrRef::BaseTableAttrRef(BaseTableAttrRef { table_id, attr_idx }) = - self.memo.get_attribute_ref(group_id, attr_ref_idx) - { - let pattern = ConstantPred::from_pred_node(pattern) - .expect("we already checked pattern is a constant") - .value() - .as_str(); - - // Compute the selectivity exculuding MCVs. - // See Postgres `like_selectivity`. - let non_mcv_sel = pattern - .chars() - .fold(1.0, |acc, c| { - if c == '%' { - acc * FULL_WILDCARD_SEL_FACTOR - } else { - acc * FIXED_CHAR_SEL_FACTOR - } - }) - .min(1.0); - - // Compute the selectivity in MCVs. - // TODO: Handle the case where `attribute_stats` is None. - let (mut mcv_freq, mut null_frac) = (0.0, 0.0); - if let Some(attribute_stats) = - self.get_attribute_comb_stats(table_id, &[attr_idx]).await? - { - (mcv_freq, null_frac) = { - let pred = Box::new(move |val: &AttributeCombValue| { - let string = - StringArray::from(vec![val[0].as_ref().unwrap().as_str().as_ref()]); - let pattern = StringArray::from(vec![pattern.as_ref()]); - like(&string, &pattern).unwrap().value(0) - }); - ( - attribute_stats.mcvs.freq_over_pred(pred), - attribute_stats.null_frac, - ) - }; - } - let result = non_mcv_sel + mcv_freq; - - Ok(if like_expr.negated() { - 1.0 - result - null_frac - } else { - result - } - // Postgres clamps the result after histogram and before MCV. See Postgres - // `patternsel_common`. - .clamp(0.0001, 0.9999)) - } else { - // TOOD: derived attribute - Ok(UNIMPLEMENTED_SEL) - } - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use crate::{ - common::values::Value, - stats::{ - utilities::{counter::Counter, simple_map::SimpleMap}, - MostCommonValues, FIXED_CHAR_SEL_FACTOR, FULL_WILDCARD_SEL_FACTOR, - }, - test_utils::tests::*, - }; - - #[tokio::test] - async fn test_like_no_nulls() { - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::SimpleFrequency(SimpleMap::new(vec![ - (vec![Some(Value::String("abcd".into()))], 0.1), - (vec![Some(Value::String("abc".into()))], 0.1), - ])), - None, - 2, - 0.0, - ); - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![None], - ); - - assert_approx_eq::assert_approx_eq!( - cost_model - .get_like_selectivity( - TEST_GROUP1_ID, - &like(TEST_ATTR1_BASE_INDEX, "%abcd%", false) - ) // TODO: Fix this - .await - .unwrap(), - 0.1 + FULL_WILDCARD_SEL_FACTOR.powi(2) * FIXED_CHAR_SEL_FACTOR.powi(4) - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_like_selectivity(TEST_GROUP1_ID, &like(TEST_ATTR1_BASE_INDEX, "%abc%", false)) // TODO: Fix this - .await - .unwrap(), - 0.1 + 0.1 + FULL_WILDCARD_SEL_FACTOR.powi(2) * FIXED_CHAR_SEL_FACTOR.powi(3) - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_like_selectivity(TEST_GROUP1_ID, &like(TEST_ATTR1_BASE_INDEX, "%abc%", true)) // TODO: Fix this - .await - .unwrap(), - 1.0 - (0.1 + 0.1 + FULL_WILDCARD_SEL_FACTOR.powi(2) * FIXED_CHAR_SEL_FACTOR.powi(3)) - ); - } - - #[tokio::test] - async fn test_like_with_nulls() { - let null_frac = 0.5; - let mut mcvs_counts = HashMap::new(); - mcvs_counts.insert(vec![Some(Value::String("abcd".into()))], 1); - let mcvs_total_count = 10; - let per_attribute_stats = TestPerAttributeStats::new( - MostCommonValues::Counter(Counter::new_from_existing(mcvs_counts, mcvs_total_count)), - None, - 2, - null_frac, - ); - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([( - TEST_ATTR1_BASE_INDEX, - per_attribute_stats, - )])], - vec![None], - ); - - assert_approx_eq::assert_approx_eq!( - cost_model - .get_like_selectivity(TEST_GROUP1_ID, &like(0, "%abcd%", false)) // TODO: Fix this - .await - .unwrap(), - 0.1 + FULL_WILDCARD_SEL_FACTOR.powi(2) * FIXED_CHAR_SEL_FACTOR.powi(4) - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_like_selectivity(TEST_GROUP1_ID, &like(0, "%abcd%", true)) // TODO: Fix this - .await - .unwrap(), - 1.0 - (0.1 + FULL_WILDCARD_SEL_FACTOR.powi(2) * FIXED_CHAR_SEL_FACTOR.powi(4)) - - null_frac - ); - } -} diff --git a/optd-cost-model/src/cost/filter/log_op.rs b/optd-cost-model/src/cost/filter/log_op.rs deleted file mode 100644 index 381584d..0000000 --- a/optd-cost-model/src/cost/filter/log_op.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::{ - common::{nodes::ArcPredicateNode, predicates::log_op_pred::LogOpType, types::GroupId}, - cost_model::CostModelImpl, - storage::CostModelStorageManager, - CostModelResult, -}; - -impl CostModelImpl { - pub(crate) async fn get_log_op_selectivity( - &self, - group_id: GroupId, - log_op_typ: LogOpType, - children: &[ArcPredicateNode], - ) -> CostModelResult { - match log_op_typ { - LogOpType::And => { - let mut and_sel = 1.0; - for child in children { - let selectivity = self.get_filter_selectivity(group_id, child.clone()).await?; - and_sel *= selectivity; - } - Ok(and_sel) - } - LogOpType::Or => { - let mut or_sel_neg = 1.0; - for child in children { - let selectivity = self.get_filter_selectivity(group_id, child.clone()).await?; - or_sel_neg *= 1.0 - selectivity; - } - Ok(1.0 - or_sel_neg) - } - } - } -} diff --git a/optd-cost-model/src/cost/filter/mod.rs b/optd-cost-model/src/cost/filter/mod.rs deleted file mode 100644 index 00ea653..0000000 --- a/optd-cost-model/src/cost/filter/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod attribute; -pub mod comp_op; -pub mod constant; -pub mod core; -pub mod in_list; -pub mod like; -pub mod log_op; diff --git a/optd-cost-model/src/cost/join/core.rs b/optd-cost-model/src/cost/join/core.rs deleted file mode 100644 index 6d10fb9..0000000 --- a/optd-cost-model/src/cost/join/core.rs +++ /dev/null @@ -1,1275 +0,0 @@ -use std::collections::HashSet; - -use itertools::Itertools; - -use crate::{ - common::{ - nodes::{ArcPredicateNode, JoinType, PredicateType, ReprPredicateNode}, - predicates::{ - attr_index_pred::AttrIndexPred, - list_pred::ListPred, - log_op_pred::{LogOpPred, LogOpType}, - }, - properties::attr_ref::{ - AttrRef, AttrRefs, BaseTableAttrRef, EqPredicate, SemanticCorrelation, - }, - types::GroupId, - }, - cost::join::get_on_attr_ref_pair, - cost_model::CostModelImpl, - stats::DEFAULT_NUM_DISTINCT, - storage::CostModelStorageManager, - CostModelResult, -}; - -impl CostModelImpl { - /// The expr_tree input must be a "mixed expression tree", just like with - /// `get_filter_selectivity`. - /// - /// This is a "wrapper" to separate the equality conditions from the filter conditions before - /// calling the "main" `get_join_selectivity_core` function. - #[allow(clippy::too_many_arguments)] - pub(crate) async fn get_join_selectivity_from_expr_tree( - &self, - join_typ: JoinType, - group_id: GroupId, - expr_tree: ArcPredicateNode, - attr_refs: &AttrRefs, - input_correlation: Option, - left_row_cnt: f64, - right_row_cnt: f64, - ) -> CostModelResult { - if expr_tree.typ == PredicateType::LogOp(LogOpType::And) { - let mut on_attr_ref_pairs = vec![]; - let mut filter_expr_trees = vec![]; - for child_expr_tree in &expr_tree.children { - if let Some(on_attr_ref_pair) = - get_on_attr_ref_pair(child_expr_tree.clone(), attr_refs) - { - on_attr_ref_pairs.push(on_attr_ref_pair) - } else { - let child_expr = child_expr_tree.clone(); - filter_expr_trees.push(child_expr); - } - } - assert!(on_attr_ref_pairs.len() + filter_expr_trees.len() == expr_tree.children.len()); - let filter_expr_tree = if filter_expr_trees.is_empty() { - None - } else { - Some(LogOpPred::new(LogOpType::And, filter_expr_trees).into_pred_node()) - }; - self.get_join_selectivity_core( - join_typ, - group_id, - on_attr_ref_pairs, - filter_expr_tree, - attr_refs, - input_correlation, - left_row_cnt, - right_row_cnt, - 0, - ) - .await - } else { - #[allow(clippy::collapsible_else_if)] - if let Some(on_attr_ref_pair) = get_on_attr_ref_pair(expr_tree.clone(), attr_refs) { - self.get_join_selectivity_core( - join_typ, - group_id, - vec![on_attr_ref_pair], - None, - attr_refs, - input_correlation, - left_row_cnt, - right_row_cnt, - 0, - ) - .await - } else { - self.get_join_selectivity_core( - join_typ, - group_id, - vec![], - Some(expr_tree), - attr_refs, - input_correlation, - left_row_cnt, - right_row_cnt, - 0, - ) - .await - } - } - } - - /// A wrapper to convert the join keys to the format expected by get_join_selectivity_core() - #[allow(clippy::too_many_arguments)] - pub(crate) async fn get_join_selectivity_from_keys( - &self, - join_typ: JoinType, - group_id: GroupId, - left_keys: ListPred, - right_keys: ListPred, - attr_refs: &AttrRefs, - input_correlation: Option, - left_row_cnt: f64, - right_row_cnt: f64, - left_attr_cnt: usize, - ) -> CostModelResult { - assert!(left_keys.len() == right_keys.len()); - // I assume that the keys are already in the right order - // s.t. the ith key of left_keys corresponds with the ith key of right_keys - let on_attr_ref_pairs = left_keys - .to_vec() - .into_iter() - .zip(right_keys.to_vec()) - .map(|(left_key, right_key)| { - ( - AttrIndexPred::from_pred_node(left_key).expect("keys should be AttrRefPreds"), - AttrIndexPred::from_pred_node(right_key).expect("keys should be AttrRefPreds"), - ) - }) - .collect_vec(); - self.get_join_selectivity_core( - join_typ, - group_id, - on_attr_ref_pairs, - None, - attr_refs, - input_correlation, - left_row_cnt, - right_row_cnt, - left_attr_cnt, - ) - .await - } - - /// The core logic of join selectivity which assumes we've already separated the expression - /// into the on conditions and the filters. - /// - /// Hash join and NLJ reference right table attributes differently, hence the - /// `right_attr_ref_offset` parameter. - /// - /// For hash join, the right table attributes indices are with respect to the right table, - /// which means #0 is the first attribute of the right table. - /// - /// For NLJ, the right table attributes indices are with respect to the output of the join. - /// For example, if the left table has 3 attributes, the first attribute of the right table - /// is #3 instead of #0. - #[allow(clippy::too_many_arguments)] - async fn get_join_selectivity_core( - &self, - join_typ: JoinType, - group_id: GroupId, - on_attr_ref_pairs: Vec<(AttrIndexPred, AttrIndexPred)>, - filter_expr_tree: Option, - attr_refs: &AttrRefs, - input_correlation: Option, - left_row_cnt: f64, - right_row_cnt: f64, - right_attr_ref_offset: usize, - ) -> CostModelResult { - let join_on_selectivity = self - .get_join_on_selectivity( - &on_attr_ref_pairs, - attr_refs, - input_correlation, - right_attr_ref_offset, - ) - .await?; - // Currently, there is no difference in how we handle a join filter and a select filter, - // so we use the same function. - // - // One difference (that we *don't* care about right now) is that join filters can contain - // expressions from multiple different tables. Currently, this doesn't affect the - // get_filter_selectivity() function, but this may change in the future. - let join_filter_selectivity = match filter_expr_tree { - Some(filter_expr_tree) => { - self.get_filter_selectivity(group_id, filter_expr_tree) - .await? - } - None => 1.0, - }; - let inner_join_selectivity = join_on_selectivity * join_filter_selectivity; - - Ok(match join_typ { - JoinType::Inner => inner_join_selectivity, - JoinType::LeftOuter => f64::max(inner_join_selectivity, 1.0 / right_row_cnt), - JoinType::RightOuter => f64::max(inner_join_selectivity, 1.0 / left_row_cnt), - JoinType::Cross => { - assert!( - on_attr_ref_pairs.is_empty(), - "Cross joins should not have on attributes" - ); - join_filter_selectivity - } - _ => unimplemented!("join_typ={} is not implemented", join_typ), - }) - } - - /// Get the selectivity of one attribute eq predicate, e.g. attrA = attrB. - async fn get_join_selectivity_from_on_attr_ref_pair( - &self, - left: &AttrRef, - right: &AttrRef, - ) -> CostModelResult { - // the formula for each pair is min(1 / ndistinct1, 1 / ndistinct2) - // (see https://postgrespro.com/blog/pgsql/5969618) - let mut ndistincts = vec![]; - for attr_ref in [left, right] { - let ndistinct = match attr_ref { - AttrRef::BaseTableAttrRef(base_attr_ref) => { - match self - .get_attribute_comb_stats(base_attr_ref.table_id, &[base_attr_ref.attr_idx]) - .await? - { - Some(per_attr_stats) => per_attr_stats.ndistinct, - None => DEFAULT_NUM_DISTINCT, - } - } - AttrRef::Derived => DEFAULT_NUM_DISTINCT, - }; - ndistincts.push(ndistinct); - } - - // using reduce(f64::min) is the idiomatic workaround to min() because - // f64 does not implement Ord due to NaN - let selectivity = ndistincts.into_iter().map(|ndistinct| 1.0 / ndistinct as f64).reduce(f64::min).expect("reduce() only returns None if the iterator is empty, which is impossible since attr_ref_exprs.len() == 2"); - assert!( - !selectivity.is_nan(), - "it should be impossible for selectivity to be NaN since n-distinct is never 0" - ); - Ok(selectivity) - } - - /// Given a set of N attributes involved in a multi-equality, find the total selectivity - /// of the multi-equality. - /// - /// This is a generalization of get_join_selectivity_from_on_attr_ref_pair(). - async fn get_join_selectivity_from_most_selective_attrs( - &self, - base_attr_refs: HashSet, - ) -> CostModelResult { - assert!(base_attr_refs.len() > 1); - let num_base_attr_refs = base_attr_refs.len(); - - let mut ndistincts = vec![]; - for base_attr_ref in base_attr_refs.iter() { - let ndistinct = match self - .get_attribute_comb_stats(base_attr_ref.table_id, &[base_attr_ref.attr_idx]) - .await? - { - Some(per_attr_stats) => per_attr_stats.ndistinct, - None => DEFAULT_NUM_DISTINCT, - }; - ndistincts.push(ndistinct); - } - - Ok(ndistincts - .into_iter() - .map(|ndistinct| 1.0 / ndistinct as f64) - .sorted_by(|a, b| { - a.partial_cmp(b) - .expect("No floats should be NaN since n-distinct is never 0") - }) - .take(num_base_attr_refs - 1) - .product()) - } - - /// A predicate set defines a "multi-equality graph", which is an unweighted undirected graph. - /// The nodes are attributes while edges are predicates. The old graph is defined by - /// `past_eq_attrs` while the `predicate` is the new addition to this graph. This - /// unweighted undirected graph consists of a number of connected components, where each - /// connected component represents attributes that are set to be equal to each other. Single - /// nodes not connected to anything are considered standalone connected components. - /// - /// The selectivity of each connected component of N nodes is equal to the product of - /// 1/ndistinct of the N-1 nodes with the highest ndistinct values. You can see this if you - /// imagine that all attributes being joined are unique attributes and that they follow the - /// inclusion principle (every element of the smaller tables is present in the larger - /// tables). When these assumptions are not true, the selectivity may not be completely - /// accurate. However, it is still fairly accurate. - /// - /// However, we cannot simply add `predicate` to the multi-equality graph and compute the - /// selectivity of the entire connected component, because this would be "double counting" a - /// lot of nodes. The join(s) before this join would already have a selectivity value. Thus, - /// we compute the selectivity of the join(s) before this join (the first block of the - /// function) and then the selectivity of the connected component after this join. The - /// quotient is the "adjustment" factor. - /// - /// NOTE: This function modifies `past_eq_attrs` by adding `predicate` to it. - async fn get_join_selectivity_adjustment_when_adding_to_multi_equality_graph( - &self, - predicate: &EqPredicate, - past_eq_attrs: &mut SemanticCorrelation, - ) -> CostModelResult { - if predicate.left == predicate.right { - // self-join, TODO: is this correct? - return Ok(1.0); - } - // To find the adjustment, we need to know the selectivity of the graph before `predicate` - // is added. - // - // There are two cases: (1) adding `predicate` does not change the # of connected - // components, and (2) adding `predicate` reduces the # of connected by 1. Note that - // attributes not involved in any predicates are considered a part of the graph and are - // a connected component on their own. - let children_pred_sel = { - if past_eq_attrs.is_eq(&predicate.left, &predicate.right) { - self.get_join_selectivity_from_most_selective_attrs( - past_eq_attrs.find_attrs_for_eq_attribute_set(&predicate.left), - ) - .await? - } else { - let left_sel = if past_eq_attrs.contains(&predicate.left) { - self.get_join_selectivity_from_most_selective_attrs( - past_eq_attrs.find_attrs_for_eq_attribute_set(&predicate.left), - ) - .await? - } else { - 1.0 - }; - let right_sel = if past_eq_attrs.contains(&predicate.right) { - self.get_join_selectivity_from_most_selective_attrs( - past_eq_attrs.find_attrs_for_eq_attribute_set(&predicate.right), - ) - .await? - } else { - 1.0 - }; - left_sel * right_sel - } - }; - - // Add predicate to past_eq_attrs and compute the selectivity of the connected component - // it creates. - past_eq_attrs.add_predicate(predicate.clone()); - let new_pred_sel = { - let attrs = past_eq_attrs.find_attrs_for_eq_attribute_set(&predicate.left); - self.get_join_selectivity_from_most_selective_attrs(attrs) - } - .await?; - - // Compute the adjustment factor. - Ok(new_pred_sel / children_pred_sel) - } - - /// Get the selectivity of the on conditions. - /// - /// Note that the selectivity of the on conditions does not depend on join type. - /// Join type is accounted for separately in get_join_selectivity_core(). - /// - /// We also check if each predicate is correlated with any of the previous predicates. - /// - /// More specifically, we are checking if the predicate can be expressed with other existing - /// predicates. E.g. if we have a predicate like A = B and B = C is equivalent to A = C. - // - /// However, we don't just throw away A = C, because we want to pick the most selective - /// predicates. For details on how we do this, see - /// `get_join_selectivity_from_redundant_predicates`. - async fn get_join_on_selectivity( - &self, - on_attr_ref_pairs: &[(AttrIndexPred, AttrIndexPred)], - attr_refs: &AttrRefs, - input_correlation: Option, - right_attr_ref_offset: usize, - ) -> CostModelResult { - let mut past_eq_attrs = input_correlation.unwrap_or_default(); - - // Multiply the selectivities of all individual conditions together - let mut selectivity = 1.0; - for on_attr_ref_pair in on_attr_ref_pairs { - let left_attr_ref = &attr_refs[on_attr_ref_pair.0.attr_index() as usize]; - let right_attr_ref = - &attr_refs[on_attr_ref_pair.1.attr_index() as usize + right_attr_ref_offset]; - - selectivity *= - if let (AttrRef::BaseTableAttrRef(left), AttrRef::BaseTableAttrRef(right)) = - (left_attr_ref, right_attr_ref) - { - let predicate = EqPredicate::new(left.clone(), right.clone()); - self.get_join_selectivity_adjustment_when_adding_to_multi_equality_graph( - &predicate, - &mut past_eq_attrs, - ) - .await? - } else { - self.get_join_selectivity_from_on_attr_ref_pair(left_attr_ref, right_attr_ref) - .await? - }; - } - - Ok(selectivity) - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use attr_ref::GroupAttrRefs; - - use crate::{ - common::{ - predicates::bin_op_pred::BinOpType, - properties::{attr_ref, Attribute}, - values::Value, - }, - stats::DEFAULT_EQ_SEL, - test_utils::tests::MemoGroupInfo, - test_utils::tests::{ - attr_index, bin_op, cnst, create_four_table_mock_cost_model, create_mock_cost_model, - create_three_table_mock_cost_model, create_two_table_mock_cost_model, - create_two_table_mock_cost_model_custom_row_cnts, empty_per_attr_stats, log_op, - per_attr_stats_with_dist_and_ndistinct, per_attr_stats_with_ndistinct, - TestOptCostModelMock, TEST_ATTR1_NAME, TEST_ATTR2_NAME, TEST_TABLE1_ID, TEST_TABLE2_ID, - TEST_TABLE3_ID, TEST_TABLE4_ID, - }, - }; - - use super::*; - - const JOIN_GROUP_ID: GroupId = GroupId(10); - - /// A wrapper around get_join_selectivity_from_expr_tree that extracts the - /// table row counts from the cost model. - async fn test_get_join_selectivity( - cost_model: &TestOptCostModelMock, - reverse_tables: bool, - join_typ: JoinType, - expr_tree: ArcPredicateNode, - attr_refs: &AttrRefs, - input_correlation: Option, - ) -> f64 { - let table1_row_cnt = cost_model.get_row_count(TEST_TABLE1_ID) as f64; - let table2_row_cnt = cost_model.get_row_count(TEST_TABLE2_ID) as f64; - - if !reverse_tables { - cost_model - .get_join_selectivity_from_expr_tree( - join_typ, - JOIN_GROUP_ID, - expr_tree, - attr_refs, - input_correlation, - table1_row_cnt, - table2_row_cnt, - ) - .await - .unwrap() - } else { - cost_model - .get_join_selectivity_from_expr_tree( - join_typ, - JOIN_GROUP_ID, - expr_tree, - attr_refs, - input_correlation, - table2_row_cnt, - table1_row_cnt, - ) - .await - .unwrap() - } - } - - #[tokio::test] - async fn test_inner_const() { - let cost_model = create_mock_cost_model( - vec![TEST_TABLE1_ID], - vec![HashMap::from([(0, empty_per_attr_stats())])], - vec![None], - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_join_selectivity_from_expr_tree( - JoinType::Inner, - JOIN_GROUP_ID, - cnst(Value::Bool(true)), - &vec![], - None, - f64::NAN, - f64::NAN - ) - .await - .unwrap(), - 1.0 - ); - assert_approx_eq::assert_approx_eq!( - cost_model - .get_join_selectivity_from_expr_tree( - JoinType::Inner, - JOIN_GROUP_ID, - cnst(Value::Bool(false)), - &vec![], - None, - f64::NAN, - f64::NAN - ) - .await - .unwrap(), - 0.0 - ); - } - - #[tokio::test] - async fn test_inner_oncond() { - let cost_model = create_two_table_mock_cost_model( - per_attr_stats_with_ndistinct(5), - per_attr_stats_with_ndistinct(4), - None, - ); - - let attr_refs = vec![ - AttrRef::base_table_attr_ref(TEST_TABLE1_ID, 0), - AttrRef::base_table_attr_ref(TEST_TABLE2_ID, 0), - ]; - let expr_tree = bin_op(BinOpType::Eq, attr_index(0), attr_index(1)); - let expr_tree_rev = bin_op(BinOpType::Eq, attr_index(1), attr_index(0)); - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree, - &attr_refs, - None, - ) - .await, - 0.2 - ); - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree_rev, - &attr_refs, - None, - ) - .await, - 0.2 - ); - } - - #[tokio::test] - async fn test_inner_and_of_onconds() { - let cost_model = create_two_table_mock_cost_model( - per_attr_stats_with_ndistinct(5), - per_attr_stats_with_ndistinct(4), - None, - ); - - let attr_refs = vec![ - AttrRef::base_table_attr_ref(TEST_TABLE1_ID, 0), - AttrRef::base_table_attr_ref(TEST_TABLE2_ID, 0), - ]; - let eq0and1 = bin_op(BinOpType::Eq, attr_index(0), attr_index(1)); - let eq1and0 = bin_op(BinOpType::Eq, attr_index(1), attr_index(0)); - let expr_tree = log_op(LogOpType::And, vec![eq0and1.clone(), eq1and0.clone()]); - let expr_tree_rev = log_op(LogOpType::And, vec![eq1and0.clone(), eq0and1.clone()]); - - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree, - &attr_refs, - None, - ) - .await, - 0.2 - ); - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree_rev, - &attr_refs, - None - ) - .await, - 0.2 - ); - } - - #[tokio::test] - async fn test_inner_and_of_oncond_and_filter() { - let join_memo = HashMap::from([( - JOIN_GROUP_ID, - MemoGroupInfo::new( - vec![ - Attribute::new_non_null_int64(TEST_ATTR1_NAME.to_string()), - Attribute::new_non_null_int64(TEST_ATTR2_NAME.to_string()), - ] - .into(), - GroupAttrRefs::new( - vec![ - AttrRef::new_base_table_attr_ref(TEST_TABLE1_ID, 0), - AttrRef::new_base_table_attr_ref(TEST_TABLE2_ID, 0), - ], - None, - ), - ), - )]); - let cost_model = create_two_table_mock_cost_model( - per_attr_stats_with_ndistinct(5), - per_attr_stats_with_ndistinct(4), - Some(join_memo), - ); - - let attr_refs = vec![ - AttrRef::base_table_attr_ref(TEST_TABLE1_ID, 0), - AttrRef::base_table_attr_ref(TEST_TABLE2_ID, 0), - ]; - let eq0and1 = bin_op(BinOpType::Eq, attr_index(0), attr_index(1)); - let eq100 = bin_op(BinOpType::Eq, attr_index(1), cnst(Value::Int32(100))); - let expr_tree = log_op(LogOpType::And, vec![eq0and1.clone(), eq100.clone()]); - let expr_tree_rev = log_op(LogOpType::And, vec![eq100.clone(), eq0and1.clone()]); - - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree, - &attr_refs, - None - ) - .await, - 0.05 - ); - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree_rev, - &attr_refs, - None - ) - .await, - 0.05 - ); - } - - #[tokio::test] - async fn test_inner_and_of_filters() { - let join_memo = HashMap::from([( - JOIN_GROUP_ID, - MemoGroupInfo::new( - vec![ - Attribute::new_non_null_int64(TEST_ATTR1_NAME.to_string()), - Attribute::new_non_null_int64(TEST_ATTR2_NAME.to_string()), - ] - .into(), - GroupAttrRefs::new( - vec![ - AttrRef::new_base_table_attr_ref(TEST_TABLE1_ID, 0), - AttrRef::new_base_table_attr_ref(TEST_TABLE2_ID, 0), - ], - None, - ), - ), - )]); - let cost_model = create_two_table_mock_cost_model( - per_attr_stats_with_ndistinct(5), - per_attr_stats_with_ndistinct(4), - Some(join_memo), - ); - - let attr_refs = vec![ - AttrRef::base_table_attr_ref(TEST_TABLE1_ID, 0), - AttrRef::base_table_attr_ref(TEST_TABLE2_ID, 0), - ]; - let neq12 = bin_op(BinOpType::Neq, attr_index(0), cnst(Value::Int32(12))); - let eq100 = bin_op(BinOpType::Eq, attr_index(1), cnst(Value::Int32(100))); - let expr_tree = log_op(LogOpType::And, vec![neq12.clone(), eq100.clone()]); - let expr_tree_rev = log_op(LogOpType::And, vec![eq100.clone(), neq12.clone()]); - - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree, - &attr_refs, - None, - ) - .await, - 0.2 - ); - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree_rev, - &attr_refs, - None - ) - .await, - 0.2 - ); - } - - #[tokio::test] - async fn test_inner_colref_eq_colref_same_table_is_not_oncond() { - let cost_model = create_two_table_mock_cost_model( - per_attr_stats_with_ndistinct(5), - per_attr_stats_with_ndistinct(4), - None, - ); - - let attr_refs = vec![ - AttrRef::base_table_attr_ref(TEST_TABLE1_ID, 0), - AttrRef::base_table_attr_ref(TEST_TABLE2_ID, 0), - ]; - let expr_tree = bin_op(BinOpType::Eq, attr_index(0), attr_index(0)); - - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree, - &attr_refs, - None - ) - .await, - DEFAULT_EQ_SEL - ); - } - - // We don't test joinsel or with oncond because if there is an oncond (on condition), the - // top-level operator must be an AND - - /// I made this helper function to avoid copying all eight lines over and over - async fn assert_outer_selectivities( - cost_model: &TestOptCostModelMock, - expr_tree: ArcPredicateNode, - expr_tree_rev: ArcPredicateNode, - attr_refs: &AttrRefs, - expected_table1_outer_sel: f64, - expected_table2_outer_sel: f64, - ) { - // all table 1 outer combinations - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - cost_model, - false, - JoinType::LeftOuter, - expr_tree.clone(), - attr_refs, - None - ) - .await, - expected_table1_outer_sel - ); - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - cost_model, - false, - JoinType::LeftOuter, - expr_tree_rev.clone(), - attr_refs, - None - ) - .await, - expected_table1_outer_sel - ); - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - cost_model, - true, - JoinType::RightOuter, - expr_tree.clone(), - attr_refs, - None - ) - .await, - expected_table1_outer_sel - ); - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - cost_model, - true, - JoinType::RightOuter, - expr_tree_rev.clone(), - attr_refs, - None - ) - .await, - expected_table1_outer_sel - ); - // all table 2 outer combinations - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - cost_model, - true, - JoinType::LeftOuter, - expr_tree.clone(), - attr_refs, - None - ) - .await, - expected_table2_outer_sel - ); - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - cost_model, - true, - JoinType::LeftOuter, - expr_tree_rev.clone(), - attr_refs, - None - ) - .await, - expected_table2_outer_sel - ); - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - cost_model, - false, - JoinType::RightOuter, - expr_tree.clone(), - attr_refs, - None - ) - .await, - expected_table2_outer_sel - ); - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - cost_model, - false, - JoinType::RightOuter, - expr_tree_rev.clone(), - attr_refs, - None - ) - .await, - expected_table2_outer_sel - ); - } - - /// Unique oncond means an oncondition on columns which are unique in both tables - /// There's only one case if both columns are unique and have different row counts: the inner - /// will be < 1 / row count of one table and = 1 / row count of another - #[tokio::test] - async fn test_outer_unique_oncond() { - let cost_model = create_two_table_mock_cost_model_custom_row_cnts( - per_attr_stats_with_ndistinct(5), - per_attr_stats_with_ndistinct(4), - 5, - 4, - None, - ); - - let attr_refs = vec![ - AttrRef::base_table_attr_ref(TEST_TABLE1_ID, 0), - AttrRef::base_table_attr_ref(TEST_TABLE2_ID, 0), - ]; - // the left/right of the join refers to the tables, not the order of columns in the - // predicate - let expr_tree = bin_op(BinOpType::Eq, attr_index(0), attr_index(1)); - let expr_tree_rev = bin_op(BinOpType::Eq, attr_index(1), attr_index(0)); - - // sanity check the expected inner sel - let expected_inner_sel = 0.2; - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree.clone(), - &attr_refs, - None - ) - .await, - expected_inner_sel - ); - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree_rev.clone(), - &attr_refs, - None - ) - .await, - expected_inner_sel - ); - // check the outer sels - assert_outer_selectivities(&cost_model, expr_tree, expr_tree_rev, &attr_refs, 0.25, 0.2) - .await; - } - - /// Non-unique oncond means the column is not unique in either table - /// Inner always >= row count means that the inner join result is >= 1 / the row count of both - /// tables - #[tokio::test] - async fn test_outer_nonunique_oncond_inner_always_geq_rowcnt() { - let cost_model = create_two_table_mock_cost_model_custom_row_cnts( - per_attr_stats_with_ndistinct(5), - per_attr_stats_with_ndistinct(4), - 10, - 8, - None, - ); - - let attr_refs = vec![ - AttrRef::base_table_attr_ref(TEST_TABLE1_ID, 0), - AttrRef::base_table_attr_ref(TEST_TABLE2_ID, 0), - ]; - // the left/right of the join refers to the tables, not the order of columns in the - // predicate - let expr_tree = bin_op(BinOpType::Eq, attr_index(0), attr_index(1)); - let expr_tree_rev = bin_op(BinOpType::Eq, attr_index(1), attr_index(0)); - - // sanity check the expected inner sel - let expected_inner_sel = 0.2; - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree.clone(), - &attr_refs, - None - ) - .await, - expected_inner_sel - ); - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree_rev.clone(), - &attr_refs, - None - ) - .await, - expected_inner_sel - ); - // check the outer sels - assert_outer_selectivities(&cost_model, expr_tree, expr_tree_rev, &attr_refs, 0.2, 0.2) - .await; - } - - /// Non-unique oncond means the column is not unique in either table - /// Inner sometimes < row count means that the inner join result < 1 / the row count of exactly - /// one table. Note that without a join filter, it's impossible to be less than the row - /// count of both tables - #[tokio::test] - async fn test_outer_nonunique_oncond_inner_sometimes_lt_rowcnt() { - let cost_model = create_two_table_mock_cost_model_custom_row_cnts( - per_attr_stats_with_ndistinct(10), - per_attr_stats_with_ndistinct(2), - 20, - 4, - None, - ); - - let attr_refs = vec![ - AttrRef::base_table_attr_ref(TEST_TABLE1_ID, 0), - AttrRef::base_table_attr_ref(TEST_TABLE2_ID, 0), - ]; - // the left/right of the join refers to the tables, not the order of columns in the - // predicate - let expr_tree = bin_op(BinOpType::Eq, attr_index(0), attr_index(1)); - let expr_tree_rev = bin_op(BinOpType::Eq, attr_index(1), attr_index(0)); - - // sanity check the expected inner sel - let expected_inner_sel = 0.1; - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree.clone(), - &attr_refs, - None - ) - .await, - expected_inner_sel - ); - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree_rev.clone(), - &attr_refs, - None - ) - .await, - expected_inner_sel - ); - // check the outer sels - assert_outer_selectivities(&cost_model, expr_tree, expr_tree_rev, &attr_refs, 0.25, 0.1) - .await; - } - - /// Unique oncond means an oncondition on columns which are unique in both tables - /// Filter means we're adding a join filter - /// There's only one case if both columns are unique and there's a filter: - /// the inner will be < 1 / row count of both tables - #[tokio::test] - async fn test_outer_unique_oncond_filter() { - let join_memo = HashMap::from([( - JOIN_GROUP_ID, - MemoGroupInfo::new( - vec![ - Attribute::new_non_null_int64(TEST_ATTR1_NAME.to_string()), - Attribute::new_non_null_int64(TEST_ATTR2_NAME.to_string()), - ] - .into(), - GroupAttrRefs::new( - vec![ - AttrRef::new_base_table_attr_ref(TEST_TABLE1_ID, 0), - AttrRef::new_base_table_attr_ref(TEST_TABLE2_ID, 0), - ], - None, - ), - ), - )]); - let cost_model = create_two_table_mock_cost_model_custom_row_cnts( - per_attr_stats_with_dist_and_ndistinct(vec![(Value::Int32(128), 0.4)], 50), - per_attr_stats_with_ndistinct(4), - 50, - 4, - Some(join_memo), - ); - - let attr_refs = vec![ - AttrRef::base_table_attr_ref(TEST_TABLE1_ID, 0), - AttrRef::base_table_attr_ref(TEST_TABLE2_ID, 0), - ]; - // the left/right of the join refers to the tables, not the order of columns in the - // predicate - let eq0and1 = bin_op(BinOpType::Eq, attr_index(0), attr_index(1)); - let eq1and0 = bin_op(BinOpType::Eq, attr_index(1), attr_index(0)); - let filter = bin_op(BinOpType::Leq, attr_index(0), cnst(Value::Int32(128))); - let expr_tree = log_op(LogOpType::And, vec![eq0and1, filter.clone()]); - // inner rev means its the inner expr (the eq op) whose children are being reversed, as - // opposed to the and op - let expr_tree_inner_rev = log_op(LogOpType::And, vec![eq1and0, filter.clone()]); - - // sanity check the expected inner sel - let expected_inner_sel = 0.008; - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree.clone(), - &attr_refs, - None - ) - .await, - expected_inner_sel - ); - assert_approx_eq::assert_approx_eq!( - test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree_inner_rev.clone(), - &attr_refs, - None - ) - .await, - expected_inner_sel - ); - // check the outer sels - assert_outer_selectivities( - &cost_model, - expr_tree, - expr_tree_inner_rev, - &attr_refs, - 0.25, - 0.02, - ) - .await; - } - - /// Test all possible permutations of three-table joins. - /// A three-table join consists of at least two joins. `join1_on_cond` is the condition of the - /// first join. There can only be one condition because only two tables are involved at - /// the time of the first join. - #[tokio::test] - #[test_case::test_case(&[(0, 1)])] - #[test_case::test_case(&[(0, 2)])] - #[test_case::test_case(&[(1, 2)])] - #[test_case::test_case(&[(0, 1), (0, 2)])] - #[test_case::test_case(&[(0, 1), (1, 2)])] - #[test_case::test_case(&[(0, 2), (1, 2)])] - #[test_case::test_case(&[(0, 1), (0, 2), (1, 2)])] - async fn test_three_table_join_for_initial_join_on_conds( - initial_join_on_conds: &[(usize, usize)], - ) { - assert!( - !initial_join_on_conds.is_empty(), - "initial_join_on_conds should be non-empty" - ); - assert_eq!( - initial_join_on_conds.len(), - initial_join_on_conds.iter().collect::>().len(), - "initial_join_on_conds shouldn't contain duplicates" - ); - let cost_model = create_three_table_mock_cost_model( - per_attr_stats_with_ndistinct(2), - per_attr_stats_with_ndistinct(3), - per_attr_stats_with_ndistinct(4), - ); - - let base_attr_refs = vec![ - BaseTableAttrRef { - table_id: TEST_TABLE1_ID, - attr_idx: 0, - }, - BaseTableAttrRef { - table_id: TEST_TABLE2_ID, - attr_idx: 0, - }, - BaseTableAttrRef { - table_id: TEST_TABLE3_ID, - attr_idx: 0, - }, - ]; - let attr_refs = base_attr_refs - .clone() - .into_iter() - .map(AttrRef::BaseTableAttrRef) - .collect(); - - let mut eq_columns = SemanticCorrelation::new(); - for initial_join_on_cond in initial_join_on_conds { - eq_columns.add_predicate(EqPredicate::new( - base_attr_refs[initial_join_on_cond.0].clone(), - base_attr_refs[initial_join_on_cond.1].clone(), - )); - } - let initial_selectivity = { - if initial_join_on_conds.len() == 1 { - let initial_join_on_cond = initial_join_on_conds.first().unwrap(); - if initial_join_on_cond == &(0, 1) { - 1.0 / 3.0 - } else if initial_join_on_cond == &(0, 2) || initial_join_on_cond == &(1, 2) { - 1.0 / 4.0 - } else { - panic!(); - } - } else { - 1.0 / 12.0 - } - }; - - let input_correlation = Some(eq_columns); - - // Try all join conditions of the final join which would lead to all three tables being - // joined. - let eq0and1 = bin_op(BinOpType::Eq, attr_index(0), attr_index(1)); - let eq0and2 = bin_op(BinOpType::Eq, attr_index(0), attr_index(2)); - let eq1and2 = bin_op(BinOpType::Eq, attr_index(1), attr_index(2)); - let and_01_02 = log_op(LogOpType::And, vec![eq0and1.clone(), eq0and2.clone()]); - let and_01_12 = log_op(LogOpType::And, vec![eq0and1.clone(), eq1and2.clone()]); - let and_02_12 = log_op(LogOpType::And, vec![eq0and2.clone(), eq1and2.clone()]); - let and_01_02_12 = log_op( - LogOpType::And, - vec![eq0and1.clone(), eq0and2.clone(), eq1and2.clone()], - ); - let mut join2_expr_trees = vec![and_01_02, and_01_12, and_02_12, and_01_02_12]; - if initial_join_on_conds.len() == 1 { - let initial_join_on_cond = initial_join_on_conds.first().unwrap(); - if initial_join_on_cond == &(0, 1) { - join2_expr_trees.push(eq0and2); - join2_expr_trees.push(eq1and2); - } else if initial_join_on_cond == &(0, 2) { - join2_expr_trees.push(eq0and1); - join2_expr_trees.push(eq1and2); - } else if initial_join_on_cond == &(1, 2) { - join2_expr_trees.push(eq0and1); - join2_expr_trees.push(eq0and2); - } else { - panic!(); - } - } - for expr_tree in join2_expr_trees { - let overall_selectivity = initial_selectivity - * test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - expr_tree.clone(), - &attr_refs, - input_correlation.clone(), - ) - .await; - assert_approx_eq::assert_approx_eq!(overall_selectivity, 1.0 / 12.0); - } - } - - #[tokio::test] - async fn test_join_which_connects_two_components_together() { - let cost_model = create_four_table_mock_cost_model( - per_attr_stats_with_ndistinct(2), - per_attr_stats_with_ndistinct(3), - per_attr_stats_with_ndistinct(4), - per_attr_stats_with_ndistinct(5), - ); - let base_attr_refs = vec![ - BaseTableAttrRef { - table_id: TEST_TABLE1_ID, - attr_idx: 0, - }, - BaseTableAttrRef { - table_id: TEST_TABLE2_ID, - attr_idx: 0, - }, - BaseTableAttrRef { - table_id: TEST_TABLE3_ID, - attr_idx: 0, - }, - BaseTableAttrRef { - table_id: TEST_TABLE4_ID, - attr_idx: 0, - }, - ]; - let attr_refs = base_attr_refs - .clone() - .into_iter() - .map(AttrRef::BaseTableAttrRef) - .collect(); - - let mut eq_columns = SemanticCorrelation::new(); - eq_columns.add_predicate(EqPredicate::new( - base_attr_refs[0].clone(), - base_attr_refs[1].clone(), - )); - eq_columns.add_predicate(EqPredicate::new( - base_attr_refs[2].clone(), - base_attr_refs[3].clone(), - )); - let initial_selectivity = 1.0 / (3.0 * 5.0); - let input_correlation = Some(eq_columns); - - let eq1and2 = bin_op(BinOpType::Eq, attr_index(1), attr_index(2)); - let overall_selectivity = initial_selectivity - * test_get_join_selectivity( - &cost_model, - false, - JoinType::Inner, - eq1and2.clone(), - &attr_refs, - input_correlation, - ) - .await; - assert_approx_eq::assert_approx_eq!(overall_selectivity, 1.0 / (3.0 * 4.0 * 5.0)); - } -} diff --git a/optd-cost-model/src/cost/join/hash_join.rs b/optd-cost-model/src/cost/join/hash_join.rs deleted file mode 100644 index f92f247..0000000 --- a/optd-cost-model/src/cost/join/hash_join.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::{ - common::{nodes::JoinType, predicates::list_pred::ListPred, types::GroupId}, - cost_model::CostModelImpl, - storage::CostModelStorageManager, - CostModelResult, EstimatedStatistic, -}; - -use super::get_input_correlation; - -impl CostModelImpl { - #[allow(clippy::too_many_arguments)] - pub async fn get_hash_join_row_cnt( - &self, - join_typ: JoinType, - group_id: GroupId, - left_row_cnt: EstimatedStatistic, - right_row_cnt: EstimatedStatistic, - left_group_id: GroupId, - right_group_id: GroupId, - left_keys: ListPred, - right_keys: ListPred, - ) -> CostModelResult { - let selectivity = { - let output_attr_refs = self.memo.get_attribute_refs(group_id); - let left_attr_refs = self.memo.get_attribute_refs(left_group_id); - let right_attr_refs = self.memo.get_attribute_refs(right_group_id); - let left_attr_cnt = left_attr_refs.attr_refs().len(); - // there may be more than one expression tree in a group. - // see comment in PredicateType::PhysicalFilter(_) for more information - let input_correlation = get_input_correlation(left_attr_refs, right_attr_refs); - self.get_join_selectivity_from_keys( - join_typ, - group_id, - left_keys, - right_keys, - output_attr_refs.attr_refs(), - input_correlation, - left_row_cnt.0, - right_row_cnt.0, - left_attr_cnt, - ) - .await? - }; - Ok(EstimatedStatistic( - (left_row_cnt.0 * right_row_cnt.0 * selectivity).max(1.0), - )) - } -} diff --git a/optd-cost-model/src/cost/join/mod.rs b/optd-cost-model/src/cost/join/mod.rs deleted file mode 100644 index 71b991b..0000000 --- a/optd-cost-model/src/cost/join/mod.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::common::{ - nodes::{ArcPredicateNode, PredicateType, ReprPredicateNode}, - predicates::{attr_index_pred::AttrIndexPred, bin_op_pred::BinOpType}, - properties::attr_ref::{ - AttrRef, AttrRefs, BaseTableAttrRef, GroupAttrRefs, SemanticCorrelation, - }, -}; - -pub mod core; -pub mod hash_join; -pub mod nested_loop_join; - -pub(crate) fn get_input_correlation( - left_prop: GroupAttrRefs, - right_prop: GroupAttrRefs, -) -> Option { - SemanticCorrelation::merge( - left_prop.output_correlation().cloned(), - right_prop.output_correlation().cloned(), - ) -} - -/// Check if an expr_tree is a join condition, returning the join on attr ref pair if it is. -/// The reason the check and the info are in the same function is because their code is almost -/// identical. It only picks out equality conditions between two attribute refs on different -/// tables -pub(crate) fn get_on_attr_ref_pair( - expr_tree: ArcPredicateNode, - attr_refs: &AttrRefs, -) -> Option<(AttrIndexPred, AttrIndexPred)> { - // 1. Check that it's equality - if expr_tree.typ == PredicateType::BinOp(BinOpType::Eq) { - let left_child = expr_tree.child(0); - let right_child = expr_tree.child(1); - // 2. Check that both sides are attribute refs - if left_child.typ == PredicateType::AttrIndex && right_child.typ == PredicateType::AttrIndex - { - // 3. Check that both sides don't belong to the same table (if we don't know, that - // means they don't belong) - let left_attr_ref_expr = AttrIndexPred::from_pred_node(left_child) - .expect("we already checked that the type is AttrRef"); - let right_attr_ref_expr = AttrIndexPred::from_pred_node(right_child) - .expect("we already checked that the type is AttrRef"); - let left_attr_ref = &attr_refs[left_attr_ref_expr.attr_index() as usize]; - let right_attr_ref = &attr_refs[right_attr_ref_expr.attr_index() as usize]; - let is_same_table = if let ( - AttrRef::BaseTableAttrRef(BaseTableAttrRef { - table_id: left_table_id, - .. - }), - AttrRef::BaseTableAttrRef(BaseTableAttrRef { - table_id: right_table_id, - .. - }), - ) = (left_attr_ref, right_attr_ref) - { - left_table_id == right_table_id - } else { - false - }; - if !is_same_table { - Some((left_attr_ref_expr, right_attr_ref_expr)) - } else { - None - } - } else { - None - } - } else { - None - } -} diff --git a/optd-cost-model/src/cost/join/nested_loop_join.rs b/optd-cost-model/src/cost/join/nested_loop_join.rs deleted file mode 100644 index ff6a2b1..0000000 --- a/optd-cost-model/src/cost/join/nested_loop_join.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::{ - common::{ - nodes::{ArcPredicateNode, JoinType}, - types::GroupId, - }, - cost_model::CostModelImpl, - storage::CostModelStorageManager, - CostModelResult, EstimatedStatistic, -}; - -use super::get_input_correlation; - -impl CostModelImpl { - #[allow(clippy::too_many_arguments)] - pub async fn get_nlj_row_cnt( - &self, - join_typ: JoinType, - group_id: GroupId, - left_row_cnt: EstimatedStatistic, - right_row_cnt: EstimatedStatistic, - left_group_id: GroupId, - right_group_id: GroupId, - join_cond: ArcPredicateNode, - ) -> CostModelResult { - let selectivity = { - let output_attr_refs = self.memo.get_attribute_refs(group_id); - let left_attr_refs = self.memo.get_attribute_refs(left_group_id); - let right_attr_refs = self.memo.get_attribute_refs(right_group_id); - let input_correlation = get_input_correlation(left_attr_refs, right_attr_refs); - - self.get_join_selectivity_from_expr_tree( - join_typ, - group_id, - join_cond, - output_attr_refs.attr_refs(), - input_correlation, - left_row_cnt.0, - right_row_cnt.0, - ) - .await? - }; - Ok(EstimatedStatistic( - (left_row_cnt.0 * right_row_cnt.0 * selectivity).max(1.0), - )) - } -} diff --git a/optd-cost-model/src/cost/limit.rs b/optd-cost-model/src/cost/limit.rs deleted file mode 100644 index c63c0e0..0000000 --- a/optd-cost-model/src/cost/limit.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::{ - common::{ - nodes::{ArcPredicateNode, ReprPredicateNode}, - predicates::constant_pred::ConstantPred, - }, - cost_model::CostModelImpl, - storage::CostModelStorageManager, - CostModelResult, EstimatedStatistic, -}; - -impl CostModelImpl { - pub(crate) fn get_limit_row_cnt( - &self, - child_row_cnt: EstimatedStatistic, - fetch_expr: ArcPredicateNode, - ) -> CostModelResult { - let fetch = ConstantPred::from_pred_node(fetch_expr) - .unwrap() - .value() - .as_u64(); - // u64::MAX represents None - if fetch == u64::MAX { - Ok(child_row_cnt) - } else { - Ok(EstimatedStatistic(child_row_cnt.0.min(fetch as f64))) - } - } -} diff --git a/optd-cost-model/src/cost/mod.rs b/optd-cost-model/src/cost/mod.rs deleted file mode 100644 index b55d449..0000000 --- a/optd-cost-model/src/cost/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod agg; -pub mod filter; -pub mod join; -pub mod limit; diff --git a/optd-cost-model/src/cost_model.rs b/optd-cost-model/src/cost_model.rs deleted file mode 100644 index 38957f9..0000000 --- a/optd-cost-model/src/cost_model.rs +++ /dev/null @@ -1,268 +0,0 @@ -#![allow(dead_code, unused_imports, unused_variables)] - -use std::sync::Arc; - -use optd_persistent::{ - cost_model::interface::{CatalogSource, Stat, StatType}, - CostModelStorageLayer, -}; - -use crate::{ - common::{ - nodes::{ArcPredicateNode, PhysicalNodeType, ReprPredicateNode}, - predicates::list_pred::ListPred, - types::{AttrId, EpochId, ExprId, TableId}, - }, - memo_ext::MemoExt, - stats::AttributeCombValueStats, - storage::{self, CostModelStorageManager}, - ComputeCostContext, Cost, CostModel, CostModelResult, EstimatedStatistic, StatValue, -}; - -/// TODO: documentation -pub struct CostModelImpl { - pub storage_manager: S, - pub default_catalog_source: CatalogSource, - pub memo: Arc, -} - -impl CostModelImpl { - /// TODO: documentation - pub fn new( - storage_manager: S, - default_catalog_source: CatalogSource, - memo: Arc, - ) -> Self { - Self { - storage_manager, - default_catalog_source, - memo, - } - } -} - -#[async_trait::async_trait] -impl CostModel for CostModelImpl { - /// TODO: should we add epoch_id? - async fn compute_operation_cost( - &self, - node: PhysicalNodeType, - predicates: &[ArcPredicateNode], - children_costs: &[Cost], - children_stats: &[EstimatedStatistic], - context: ComputeCostContext, - ) -> CostModelResult { - let res = self.storage_manager.get_cost(context.expr_id).await; - if let Ok((Some(cost), _)) = res { - return Ok(cost); - }; - let mut output_statistic = None; - if let Ok((_, Some(statistic))) = res { - output_statistic = Some(statistic); - }; - let output_cost = match node { - PhysicalNodeType::PhysicalScan => { - let output_statistic_data = output_statistic.unwrap_or( - self.derive_statistics( - node, - predicates, - children_stats, - context.clone(), - false, - ) - .await?, - ); - output_statistic = Some(output_statistic_data.clone()); - Cost { - compute_cost: 0.0, - io_cost: output_statistic_data.0, - } - } - PhysicalNodeType::PhysicalEmptyRelation => Cost { - compute_cost: 0.1, - io_cost: 0.0, - }, - PhysicalNodeType::PhysicalLimit => Cost { - compute_cost: children_costs[0].compute_cost, - io_cost: 0.0, - }, - PhysicalNodeType::PhysicalFilter => Cost { - // TODO: now this equation is specific to optd, and try to make this equation more general - compute_cost: children_costs[1].compute_cost * children_stats[0].0, - io_cost: 0.0, - }, - PhysicalNodeType::PhysicalNestedLoopJoin(join_typ) => { - let child_compute_cost = children_costs[2].compute_cost; - Cost { - compute_cost: children_stats[0].0 * children_stats[1].0 * child_compute_cost - + children_stats[0].0, - io_cost: 0.0, - } - } - // TODO: we should document that the first child is the left table, which is used to build - // the hash table. - PhysicalNodeType::PhysicalHashJoin(join_typ) => Cost { - compute_cost: children_stats[0].0 * 2.0 + children_stats[1].0, - io_cost: 0.0, - }, - PhysicalNodeType::PhysicalAgg => Cost { - compute_cost: children_stats[0].0 - * (children_costs[1].compute_cost + children_costs[2].compute_cost), - io_cost: 0.0, - }, - PhysicalNodeType::PhysicalProjection => Cost { - compute_cost: children_stats[0].0 * children_costs[1].compute_cost, - io_cost: 0.0, - }, - PhysicalNodeType::PhysicalSort => Cost { - compute_cost: children_stats[0].0 * children_stats[0].0.ln_1p().max(1.0), - io_cost: 0.0, - }, - }; - let res = self - .storage_manager - .store_cost( - context.expr_id, - Some(output_cost.clone()), - output_statistic, - None, - ) - .await; - if res.is_err() { - eprintln!("Failed to store output cost"); - } - Ok(output_cost) - } - - /// TODO: should we add epoch_id? - async fn derive_statistics( - &self, - node: PhysicalNodeType, - predicates: &[ArcPredicateNode], - children_statistics: &[EstimatedStatistic], - context: ComputeCostContext, - store_output_statistic: bool, - ) -> CostModelResult { - let res = self.storage_manager.get_cost(context.expr_id).await; - if let Ok((_, Some(statistic))) = res { - return Ok(statistic); - } - let output_statistic = match node { - PhysicalNodeType::PhysicalScan => { - let table_id = TableId(predicates[0].data.as_ref().unwrap().as_u64()); - let row_cnt = self - .storage_manager - .get_table_row_count(table_id) - .await? - .unwrap_or(1) as f64; - Ok(EstimatedStatistic(row_cnt)) - } - PhysicalNodeType::PhysicalEmptyRelation => Ok(EstimatedStatistic(0.01)), - PhysicalNodeType::PhysicalLimit => { - self.get_limit_row_cnt(children_statistics[0].clone(), predicates[1].clone()) - } - PhysicalNodeType::PhysicalFilter => { - self.get_filter_row_cnt( - children_statistics[0].clone(), - context.group_id, - predicates[0].clone(), - ) - .await - } - PhysicalNodeType::PhysicalNestedLoopJoin(join_typ) => { - self.get_nlj_row_cnt( - join_typ, - context.group_id, - children_statistics[0].clone(), - children_statistics[1].clone(), - context.children_group_ids[0], - context.children_group_ids[1], - predicates[0].clone(), - ) - .await - } - PhysicalNodeType::PhysicalHashJoin(join_typ) => { - self.get_hash_join_row_cnt( - join_typ, - context.group_id, - children_statistics[0].clone(), - children_statistics[1].clone(), - context.children_group_ids[0], - context.children_group_ids[1], - ListPred::from_pred_node(predicates[0].clone()).unwrap(), - ListPred::from_pred_node(predicates[1].clone()).unwrap(), - ) - .await - } - PhysicalNodeType::PhysicalAgg => { - self.get_agg_row_cnt(context.group_id, predicates[1].clone()) - .await - } - PhysicalNodeType::PhysicalSort | PhysicalNodeType::PhysicalProjection => { - Ok(children_statistics[0].clone()) - } - }?; - if store_output_statistic { - let res = self - .storage_manager - .store_cost(context.expr_id, None, Some(output_statistic.clone()), None) - .await; - if res.is_err() { - eprintln!("Failed to store output statistic"); - } - }; - Ok(output_statistic) - } - - async fn update_statistics( - &self, - stats: Vec, - source: String, - data: String, - ) -> CostModelResult<()> { - todo!() - } - - async fn get_table_statistic_for_analysis( - &self, - table_id: TableId, - stat_type: StatType, - epoch_id: Option, - ) -> CostModelResult> { - todo!() - } - - async fn get_attribute_statistic_for_analysis( - &self, - attr_ids: Vec, - stat_type: StatType, - epoch_id: Option, - ) -> CostModelResult> { - todo!() - } - - async fn get_cost_for_analysis( - &self, - expr_id: ExprId, - epoch_id: Option, - ) -> CostModelResult> { - todo!() - } -} - -impl CostModelImpl { - /// TODO: documentation - /// TODO: if we have memory cache, - /// we should add the reference. (&AttributeCombValueStats) - pub(crate) async fn get_attribute_comb_stats( - &self, - table_id: TableId, - attr_comb: &[u64], - ) -> CostModelResult> { - self.storage_manager - .get_attributes_comb_statistics(table_id, attr_comb) - .await - } -} - -// TODO: Add tests for `derive_statistic`` and `compute_operation_cost`. diff --git a/optd-cost-model/src/lib.rs b/optd-cost-model/src/lib.rs deleted file mode 100644 index 4b65038..0000000 --- a/optd-cost-model/src/lib.rs +++ /dev/null @@ -1,191 +0,0 @@ -use common::{ - nodes::{ArcPredicateNode, PhysicalNodeType}, - types::{AttrId, EpochId, ExprId, GroupId, TableId}, -}; -use optd_persistent::{ - cost_model::interface::{Stat, StatType}, - BackendError, -}; - -pub mod common; -pub mod cost; -pub mod cost_model; -pub mod memo_ext; -pub mod stats; -pub mod storage; -pub mod test_utils; -pub mod utils; - -pub enum StatValue { - Int(i64), - Float(f64), - String(String), -} - -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)] -pub struct ComputeCostContext { - pub group_id: GroupId, - pub expr_id: ExprId, - pub children_group_ids: Vec, -} - -#[derive(Default, Clone, Debug, PartialOrd, PartialEq)] -pub struct Cost { - pub compute_cost: f64, - pub io_cost: f64, -} - -impl From for optd_persistent::cost_model::interface::Cost { - fn from(c: Cost) -> optd_persistent::cost_model::interface::Cost { - Self { - compute_cost: c.compute_cost, - io_cost: c.io_cost, - } - } -} - -impl From for Cost { - fn from(c: optd_persistent::cost_model::interface::Cost) -> Cost { - Self { - compute_cost: c.compute_cost, - io_cost: c.io_cost, - } - } -} - -/// Estimated statistic calculated by the cost model. -/// It is the estimated output row count of the targeted expression. -#[derive(PartialEq, PartialOrd, Clone, Debug)] -pub struct EstimatedStatistic(pub f64); - -impl From for f32 { - fn from(e: EstimatedStatistic) -> f32 { - e.0 as f32 - } -} - -impl From for f64 { - fn from(e: EstimatedStatistic) -> f64 { - e.0 - } -} - -impl From for EstimatedStatistic { - fn from(f: f32) -> EstimatedStatistic { - Self(f as f64) - } -} - -pub type CostModelResult = Result; - -#[derive(Debug)] -pub enum SemanticError { - // TODO: Add more error types - UnknownStatisticType, - VersionedStatisticNotFound, - AttributeNotFound(TableId, u64), // (table_id, attribute_base_index) - // FIXME: not sure if this should be put here - InvalidPredicate(String), -} - -#[derive(Debug)] -pub enum CostModelError { - ORMError(BackendError), - SemanticError(SemanticError), - SerdeError(serde_json::Error), -} - -impl From for CostModelError { - fn from(err: BackendError) -> Self { - CostModelError::ORMError(err) - } -} - -impl From for CostModelError { - fn from(err: SemanticError) -> Self { - CostModelError::SemanticError(err) - } -} - -impl From for CostModelError { - fn from(err: serde_json::Error) -> Self { - CostModelError::SerdeError(err) - } -} - -#[async_trait::async_trait] -pub trait CostModel: 'static + Send + Sync { - /// TODO: documentation - async fn compute_operation_cost( - &self, - node: PhysicalNodeType, - predicates: &[ArcPredicateNode], - children_costs: &[Cost], - children_stats: &[EstimatedStatistic], - context: ComputeCostContext, - ) -> CostModelResult; - - /// TODO: documentation - /// It is for cardinality estimation. The output should be the estimated - /// statistic calculated by the cost model. - /// If this method is called by `compute_operation_cost`, please set - /// `store_output_statistic` to `false`; if it is called by the optimizer, - /// please set `store_output_statistic` to `true`. Since we can store the - /// estimated statistic and cost by calling the ORM method once. - /// - /// TODO: I am not sure whether to introduce `store_output_statistic`, since - /// it add complexity to the interface, considering currently only Scan needs - /// the output row count to calculate the costs. So updating the database twice - /// seems cheap. But in the future, maybe more cost computations rely on the output - /// row count. (Of course, it should be removed if we separate the cost and - /// estimated_statistic into 2 tables.) - /// - /// TODO: Consider make it a helper function, so we can store Cost in the - /// ORM more easily. - /// - /// TODO: I would suggest to rename this method to `derive_row_count`, since - /// statistic is easily to be confused with the real statistic. - /// Also we need to update other places to use estimated statistic to row count, - /// either in this crate or in optd-persistent. - async fn derive_statistics( - &self, - node: PhysicalNodeType, - predicates: &[ArcPredicateNode], - children_stats: &[EstimatedStatistic], - context: ComputeCostContext, - store_output_statistic: bool, - ) -> CostModelResult; - - /// TODO: documentation - /// It is for **REAL** statistic updates, not for estimated statistics. - /// TODO: Change data from String to other types. - async fn update_statistics( - &self, - stats: Vec, - source: String, - data: String, - ) -> CostModelResult<()>; - - /// TODO: documentation - async fn get_table_statistic_for_analysis( - &self, - table_id: TableId, - stat_type: StatType, - epoch_id: Option, - ) -> CostModelResult>; - - /// TODO: documentation - async fn get_attribute_statistic_for_analysis( - &self, - attr_ids: Vec, - stat_type: StatType, - epoch_id: Option, - ) -> CostModelResult>; - - /// TODO: documentation - async fn get_cost_for_analysis( - &self, - expr_id: ExprId, - epoch_id: Option, - ) -> CostModelResult>; -} diff --git a/optd-cost-model/src/memo_ext.rs b/optd-cost-model/src/memo_ext.rs deleted file mode 100644 index 78d4225..0000000 --- a/optd-cost-model/src/memo_ext.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::common::{ - properties::{ - attr_ref::{AttrRef, GroupAttrRefs}, - schema::Schema, - Attribute, - }, - types::GroupId, -}; - -/// [`MemoExt`] is a trait that provides methods to access the schema, column reference, and attribute -/// information of a group in the memo. The information are used by the cost model to compute the cost of -/// an expression. -/// -/// [`MemoExt`] should be implemented by the optimizer core to provide the necessary information to the cost -/// model. All information required here is already present in the memo, so the optimizer core should be able -/// to implement this trait without additional work. -pub trait MemoExt: Send + Sync + 'static { - /// Get the schema of a group in the memo. - fn get_schema(&self, group_id: GroupId) -> Schema; - /// Get the attribute info of a given attribute in a group in the memo. - fn get_attribute_info(&self, group_id: GroupId, attr_ref_idx: u64) -> Attribute; - /// Get the attribute reference of a group in the memo. - fn get_attribute_refs(&self, group_id: GroupId) -> GroupAttrRefs; - /// Get the attribute reference of a given attribute in a group in the memo. - fn get_attribute_ref(&self, group_id: GroupId, attr_ref_idx: u64) -> AttrRef; - - // TODO: Figure out what other information is needed to compute the cost... -} diff --git a/optd-cost-model/src/stats/arith_encoder.rs b/optd-cost-model/src/stats/arith_encoder.rs deleted file mode 100644 index 730fcdb..0000000 --- a/optd-cost-model/src/stats/arith_encoder.rs +++ /dev/null @@ -1,69 +0,0 @@ -//! This module provides an encoder that converts alpha-numeric strings -//! into f64 values, designed to maintain the natural ordering of strings. -//! -//! While the encoding is theoretically lossless, in practice, it may suffer -//! from precision loss due to floating-point errors. -//! -//! Non-alpha-numeric characters are relegated to the end of the encoded value, -//! rendering them indistinguishable from one another in this context. - -use std::{collections::HashMap, sync::LazyLock}; - -// The alphanumerical ordering. -const ALPHANUMERIC_ORDER: [char; 95] = [ - ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', - '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~', '0', '1', '2', '3', '4', - '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', - 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', -]; - -const PMF: f64 = 1.0 / (ALPHANUMERIC_ORDER.len() as f64); - -static CDF: LazyLock> = LazyLock::new(|| { - let length = ALPHANUMERIC_ORDER.len() + 1; // To account for non-alpha-numeric characters. - let mut cdf = HashMap::with_capacity(length); - for (index, &char) in ALPHANUMERIC_ORDER.iter().enumerate() { - cdf.insert(char, (index as f64) / (length as f64)); - } - cdf -}); - -pub fn encode(string: &str) -> f64 { - let mut left = 0.0; - // 10_000.0 is fairly arbitrary. don't make it f64::MAX though because it causes overflow in - // other places of the code - let mut right = 10_000.0; - - for char in string.chars() { - let cdf = CDF.get(&char).unwrap_or(&1.0); - let distance = right - left; - right = left + distance * (cdf + PMF); - left += distance * cdf; - } - - left -} - -// Start of unit testing section. -#[cfg(test)] -mod tests { - use super::encode; - - #[test] - fn encode_tests() { - assert!(encode("") < encode("abc")); - assert!(encode("abc") < encode("bcd")); - - assert!(encode("a") < encode("aaa")); - assert!(encode("!a") < encode("a!")); - assert!(encode("Alexis") < encode("Schlomer")); - - assert!(encode("Gungnir Rules!") < encode("Schlomer")); - assert!(encode("Gungnir Rules!") < encode("Schlomer")); - - assert_eq!(encode(" "), encode(" ")); - assert_eq!(encode("Same"), encode("Same")); - assert!(encode("Nicolas ") < encode("Nicolas💰💼")); - } -} diff --git a/optd-cost-model/src/stats/mod.rs b/optd-cost-model/src/stats/mod.rs deleted file mode 100644 index 7ec2510..0000000 --- a/optd-cost-model/src/stats/mod.rs +++ /dev/null @@ -1,211 +0,0 @@ -#![allow(unused)] - -mod arith_encoder; -pub mod utilities; - -use crate::common::values::Value; -use serde::{Deserialize, Serialize}; -use utilities::counter::Counter; -use utilities::{ - simple_map::{self, SimpleMap}, - tdigest::TDigest, -}; - -// Default n-distinct estimate for derived columns or columns lacking statistics -pub const DEFAULT_NUM_DISTINCT: u64 = 200; -// A placeholder for unimplemented!() for codepaths which are accessed by plannertest -pub const UNIMPLEMENTED_SEL: f64 = 0.01; -// Default statistics. All are from selfuncs.h in Postgres unless specified otherwise -// Default selectivity estimate for equalities such as "A = b" -pub const DEFAULT_EQ_SEL: f64 = 0.005; -// Default selectivity estimate for inequalities such as "A < b" -pub const DEFAULT_INEQ_SEL: f64 = 0.3333333333333333; -// Used for estimating pattern selectivity character-by-character. These numbers -// are not used on their own. Depending on the characters in the pattern, the -// selectivity is multiplied by these factors. -// -// See `FULL_WILDCARD_SEL` and `FIXED_CHAR_SEL` in Postgres. -pub const FULL_WILDCARD_SEL_FACTOR: f64 = 5.0; -pub const FIXED_CHAR_SEL_FACTOR: f64 = 0.2; - -pub type AttributeCombValue = Vec>; - -// TODO: remove the clone, see the comment in the [`AttributeCombValueStats`] -#[derive(Serialize, Deserialize, Debug, Clone)] -#[serde(tag = "type")] -pub enum MostCommonValues { - Counter(Counter), - SimpleFrequency(SimpleMap), - // Add more types here... -} - -impl MostCommonValues { - // it is true that we could just expose freq_over_pred() and use that for freq() and - // total_freq() however, freq() and total_freq() each have potential optimizations (freq() - // is O(1) instead of O(n) and total_freq() can be cached) - // additionally, it makes sense to return an Option for freq() instead of just 0 if value - // doesn't exist thus, I expose three different functions - pub fn freq(&self, value: &AttributeCombValue) -> Option { - match self { - MostCommonValues::Counter(counter) => counter.frequencies().get(value).copied(), - MostCommonValues::SimpleFrequency(simple_map) => simple_map.m.get(value).copied(), - } - } - - pub fn total_freq(&self) -> f64 { - match self { - MostCommonValues::Counter(counter) => counter.frequencies().values().sum(), - MostCommonValues::SimpleFrequency(simple_map) => simple_map.m.values().sum(), - } - } - - pub fn freq_over_pred(&self, pred: Box bool>) -> f64 { - match self { - MostCommonValues::Counter(counter) => counter - .frequencies() - .iter() - .filter(|(val, _)| pred(val)) - .map(|(_, freq)| freq) - .sum(), - MostCommonValues::SimpleFrequency(simple_map) => simple_map - .m - .iter() - .filter(|(val, _)| pred(val)) - .map(|(_, freq)| freq) - .sum(), - } - } - - // returns the # of entries (i.e. value + freq) in the most common values structure - pub fn cnt(&self) -> usize { - match self { - MostCommonValues::Counter(counter) => counter.frequencies().len(), - MostCommonValues::SimpleFrequency(simple_map) => simple_map.m.len(), - } - } - - pub fn empty() -> Self { - MostCommonValues::SimpleFrequency(SimpleMap::new(vec![])) - } -} - -// TODO: remove the clone, see the comment in the [`AttributeCombValueStats`] -#[derive(Serialize, Deserialize, Debug, Clone)] -#[serde(tag = "type")] -pub enum Distribution { - TDigest(TDigest), - SimpleDistribution(SimpleMap), - // Add more types here... -} - -impl Distribution { - pub fn cdf(&self, value: &Value) -> f64 { - match self { - Distribution::TDigest(tdigest) => { - let nb_rows = tdigest.norm_weight; - if nb_rows == 0 { - tdigest.cdf(value) - } else { - tdigest.centroids.len() as f64 * tdigest.cdf(value) / nb_rows as f64 - } - } - Distribution::SimpleDistribution(simple_distribution) => { - *simple_distribution.m.get(value).unwrap_or(&0.0) - } - } - } - - pub fn empty() -> Self { - Distribution::SimpleDistribution(SimpleMap::new(vec![])) - } -} - -// TODO: Remove the clone. Now I have to add this because -// persistent.rs doesn't have a memory cache, so we have to -// return AttributeCombValueStats rather than &AttributeCombValueStats. -// But this poses a problem for mock.rs when testing, since mock storage -// only has memory hash map, so we need to return a clone of AttributeCombValueStats. -// Later, if memory cache is added, we should change this to return a reference. -// **and** remove the clone. -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct AttributeCombValueStats { - pub mcvs: MostCommonValues, // Does NOT contain full nulls. - pub distr: Option, // Does NOT contain mcvs; optional. - pub ndistinct: u64, // Does NOT contain full nulls. - pub null_frac: f64, // % of full nulls. -} - -impl AttributeCombValueStats { - pub fn new( - mcvs: MostCommonValues, - distr: Option, - ndistinct: u64, - null_frac: f64, - ) -> Self { - Self { - mcvs, - ndistinct, - null_frac, - distr, - } - } -} - -#[cfg(test)] -mod tests { - use super::{Counter, MostCommonValues}; - use crate::{common::values::Value, stats::AttributeCombValue}; - use serde_json::json; - - #[test] - fn test_most_common_values() { - let elem1 = vec![Some(Value::Int32(1))]; - let elem2 = vec![Some(Value::Int32(2))]; - let mut counter = Counter::new(&[elem1.clone(), elem2.clone()]); - - let elems = vec![elem2.clone(), elem1.clone(), elem2.clone(), elem2.clone()]; - counter.aggregate(&elems); - - let mcvs = MostCommonValues::Counter(counter); - assert_eq!(mcvs.freq(&elem1), Some(0.25)); - assert_eq!(mcvs.freq(&elem2), Some(0.75)); - assert_eq!(mcvs.total_freq(), 1.0); - - let elem1_cloned = elem1.clone(); - let pred1 = Box::new(move |x: &AttributeCombValue| x == &elem1_cloned); - let pred2 = Box::new(move |x: &AttributeCombValue| x != &elem1); - assert_eq!(mcvs.freq_over_pred(pred1), 0.25); - assert_eq!(mcvs.freq_over_pred(pred2), 0.75); - - assert_eq!(mcvs.cnt(), 2); - } - - #[test] - fn test_most_common_values_serde() { - let elem1 = vec![Some(Value::Int32(1))]; - let elem2 = vec![Some(Value::Int32(2))]; - let mut counter = Counter::new(&[elem1.clone(), elem2.clone()]); - - let elems = vec![elem2.clone(), elem1.clone(), elem2.clone(), elem2.clone()]; - counter.aggregate(&elems); - - let mcvs = MostCommonValues::Counter(counter); - let serialized = serde_json::to_value(&mcvs).unwrap(); - println!("serialized: {:?}", serialized); - - let deserialized: MostCommonValues = serde_json::from_value(serialized).unwrap(); - assert_eq!(mcvs.freq(&elem1), Some(0.25)); - assert_eq!(mcvs.freq(&elem2), Some(0.75)); - assert_eq!(mcvs.total_freq(), 1.0); - - let elem1_cloned = elem1.clone(); - let pred1 = Box::new(move |x: &AttributeCombValue| x == &elem1_cloned); - let pred2 = Box::new(move |x: &AttributeCombValue| x != &elem1); - assert_eq!(mcvs.freq_over_pred(pred1), 0.25); - assert_eq!(mcvs.freq_over_pred(pred2), 0.75); - - assert_eq!(mcvs.cnt(), 2); - } - - // TODO: Add tests for Distribution -} diff --git a/optd-cost-model/src/stats/utilities/counter.rs b/optd-cost-model/src/stats/utilities/counter.rs deleted file mode 100644 index 368700c..0000000 --- a/optd-cost-model/src/stats/utilities/counter.rs +++ /dev/null @@ -1,204 +0,0 @@ -use std::collections::HashMap; -use std::hash::Hash; - -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; - -/// The Counter structure to track exact frequencies of fixed elements. -/// TODO: remove the clone, see the comment in the [`AttributeCombValueStats`] -#[serde_with::serde_as] -#[derive(Default, Serialize, Deserialize, Debug, Clone)] -pub struct Counter { - #[serde_as(as = "HashMap")] - counts: HashMap, // The exact counts of an element T. - total_count: i32, // The total number of elements. -} - -// Self-contained implementation of the Counter data structure. -impl Counter -where - T: PartialEq + Eq + Hash + Clone + Serialize + DeserializeOwned, -{ - /// Creates and initializes a new empty Counter with the frequency map sized - /// based on the number of unique elements in `to_track`. - pub fn new(to_track: &[T]) -> Self { - let mut counts: HashMap = HashMap::with_capacity(to_track.len()); - for item in to_track { - counts.insert(item.clone(), 0); - } - - Counter:: { - counts, - total_count: 0, - } - } - - pub fn new_from_existing(counts: HashMap, total_count: i32) -> Self { - Counter:: { - counts, - total_count, - } - } - - // Inserts an element in the Counter if it is being tracked. - fn insert_element(&mut self, elem: T, occ: i32) { - if let Some(frequency) = self.counts.get_mut(&elem) { - *frequency += occ; - } - } - - /// Digests an array of data into the Counter structure. - pub fn aggregate(&mut self, data: &[T]) { - data.iter() - .for_each(|key| self.insert_element(key.clone(), 1)); - self.total_count += data.len() as i32; - } - - /// Merges another Counter into the current one. - /// Particularly useful for parallel execution. - pub fn merge(&mut self, other: &Counter) { - other - .counts - .iter() - .for_each(|(key, occ)| self.insert_element(key.clone(), *occ)); - self.total_count += other.total_count; - } - - /// Returns the frequencies of the most common values. - pub fn frequencies(&self) -> HashMap { - self.counts - .iter() - .map(|(key, &value)| (key.clone(), value as f64 / self.total_count as f64)) - .collect() - } - - /// Whether the counter tracks the given key. - pub fn is_tracking(&self, key: &T) -> bool { - self.counts.contains_key(key) - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - use std::sync::{Arc, Mutex}; - - use crossbeam::thread; - use rand::rngs::StdRng; - use rand::seq::SliceRandom; - use rand::SeedableRng; - - use super::Counter; - - // Generates hardcoded frequencies and returns them, - // along with a flattened randomized array containing those frequencies. - fn generate_frequencies() -> (HashMap, Vec) { - let mut frequencies = HashMap::new(); - - frequencies.insert(0, 2); - frequencies.insert(1, 4); - frequencies.insert(2, 9); - frequencies.insert(3, 8); - frequencies.insert(4, 50); - frequencies.insert(5, 6); - - let mut flattened = Vec::new(); - for (key, &value) in &frequencies { - for _ in 0..value { - flattened.push(*key); - } - } - - let mut rng = StdRng::seed_from_u64(0); - flattened.shuffle(&mut rng); - - (frequencies, flattened) - } - - #[test] - fn aggregate() { - let to_track = vec![0, 1, 2, 3]; - let mut mcv = Counter::::new(&to_track); - - let (frequencies, flattened) = generate_frequencies(); - - mcv.aggregate(&flattened); - - let mcv_freq = mcv.frequencies(); - assert_eq!(mcv_freq.len(), to_track.len()); - - to_track.iter().for_each(|item| { - assert!(mcv_freq.contains_key(item)); - assert_eq!( - mcv_freq.get(item), - frequencies - .get(item) - .map(|e| (*e as f64 / flattened.len() as f64)) - .as_ref() - ); - }); - } - - #[test] - fn merge() { - let to_track = vec![0, 1, 2, 3]; - let n_jobs = 16; - - let total_frequencies = Arc::new(Mutex::new(HashMap::::new())); - let total_count = Arc::new(Mutex::new(0)); - let result_mcv = Arc::new(Mutex::new(Counter::::new(&to_track))); - thread::scope(|s| { - for _ in 0..n_jobs { - s.spawn(|_| { - let mut local_mcv = Counter::::new(&to_track); - - let (local_frequencies, flattened) = generate_frequencies(); - let mut total_frequencies = total_frequencies.lock().unwrap(); - let mut total_count = total_count.lock().unwrap(); - for (&key, &value) in &local_frequencies { - *total_frequencies.entry(key).or_insert(0) += value; - *total_count += value; - } - - local_mcv.aggregate(&flattened); - - let mcv_local_freq = local_mcv.frequencies(); - assert_eq!(mcv_local_freq.len(), to_track.len()); - - to_track.iter().for_each(|item| { - assert!(mcv_local_freq.contains_key(item)); - assert_eq!( - mcv_local_freq.get(item), - local_frequencies - .get(item) - .map(|e| (*e as f64 / flattened.len() as f64)) - .as_ref() - ); - }); - - let mut result = result_mcv.lock().unwrap(); - result.merge(&local_mcv); - }); - } - }) - .unwrap(); - - let mcv = result_mcv.lock().unwrap(); - let total_count = total_count.lock().unwrap(); - let mcv_freq = mcv.frequencies(); - - assert_eq!(*total_count, mcv.total_count); - to_track.iter().for_each(|item| { - assert!(mcv_freq.contains_key(item)); - assert_eq!( - mcv_freq.get(item), - total_frequencies - .lock() - .unwrap() - .get(item) - .map(|e| (*e as f64 / *total_count as f64)) - .as_ref() - ); - }); - } -} diff --git a/optd-cost-model/src/stats/utilities/mod.rs b/optd-cost-model/src/stats/utilities/mod.rs deleted file mode 100644 index 0a7903b..0000000 --- a/optd-cost-model/src/stats/utilities/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod counter; -pub mod simple_map; -pub mod tdigest; diff --git a/optd-cost-model/src/stats/utilities/simple_map.rs b/optd-cost-model/src/stats/utilities/simple_map.rs deleted file mode 100644 index d04439e..0000000 --- a/optd-cost-model/src/stats/utilities/simple_map.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::collections::HashMap; -use std::hash::Hash; - -use serde::{Deserialize, Serialize}; - -use crate::common::values::Value; - -/// TODO: documentation -/// Now it is mainly for testing purposes. -#[derive(Clone, Serialize, Deserialize, Debug, Default)] -pub struct SimpleMap { - pub(crate) m: HashMap, -} - -impl SimpleMap { - pub fn new(v: Vec<(K, f64)>) -> Self { - Self { - m: v.into_iter().collect(), - } - } -} diff --git a/optd-cost-model/src/stats/utilities/tdigest.rs b/optd-cost-model/src/stats/utilities/tdigest.rs deleted file mode 100644 index 96a2269..0000000 --- a/optd-cost-model/src/stats/utilities/tdigest.rs +++ /dev/null @@ -1,393 +0,0 @@ -// Copyright (c) 2023-2024 CMU Database Group -// -// Use of this source code is governed by an MIT-style license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. - -//! Simplified implementation of the TDigest data structure as described in -//! Ted Dunning's paper: -//! "Computing Extremely Accurate Quantiles Using t-Digests" (2019). -//! For more details, refer to: https://arxiv.org/pdf/1902.04023.pdf - -use std::f64::consts::PI; -use std::hash::Hash; -use std::marker::PhantomData; - -use itertools::Itertools; -use serde::{Deserialize, Serialize}; - -use crate::{common::values::Value, stats::arith_encoder}; - -pub const DEFAULT_COMPRESSION: f64 = 200.0; - -/// Trait to transform any object into a stream of bytes. -pub trait IntoFloat { - fn to_float(&self) -> f64; -} - -/// The TDigest structure for the statistical aggregator to query quantiles. -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct TDigest { - /// A sorted array of Centroids, according to their mean. - pub centroids: Vec, /* TODO(Alexis): Temporary fix to normalize the stats in - * stats.rs [pub]. */ - /// Compression factor: higher is more precise, but has higher memory requirements. - compression: f64, - /// Number of values in the TDigest (sum of all centroids). - total_weight: usize, - - // TODO(Alexis): Temporary fix to normalize the stats in stats.rs [field]. - pub norm_weight: usize, - - data_type: PhantomData, // For type checker. -} - -/// A Centroid is a cluster of aggregated data points. -#[derive(PartialEq, PartialOrd, Clone, Serialize, Deserialize, Debug)] -pub struct Centroid { - // TODO(Alexis): Temporary fix to normalize the stats in stats.rs [pub]. - /// Mean of all aggregated points in this cluster. - mean: f64, - /// The number of points in this cluster. - weight: usize, -} - -// Utility functions defined on a Centroid. -impl Centroid { - // Merges an existing Centroid into itself. - fn merge(&mut self, other: &Centroid) { - let weight = self.weight + other.weight; - self.mean = - ((self.mean * self.weight as f64) + (other.mean * other.weight as f64)) / weight as f64; - self.weight = weight; - } -} - -// IntoFloat implementation of optd's Value. -impl IntoFloat for Value { - fn to_float(&self) -> f64 { - match self { - Value::UInt8(v) => *v as f64, - Value::UInt16(v) => *v as f64, - Value::UInt32(v) => *v as f64, - Value::UInt64(v) => *v as f64, - Value::Int8(v) => *v as f64, - Value::Int16(v) => *v as f64, - Value::Int32(v) => *v as f64, - Value::Int64(v) => *v as f64, - Value::Float(v) => *v.0, - Value::Bool(v) => *v as i64 as f64, - Value::String(v) => arith_encoder::encode(v), - Value::Date32(v) => *v as f64, - _ => unreachable!(), - } - } -} - -// Self-contained implementation of the TDigest data structure. -impl TDigest -where - T: IntoFloat + Eq + Hash + Clone, -{ - /// Creates and initializes a new empty TDigest. - pub fn new(compression: f64) -> Self { - TDigest { - centroids: Vec::new(), - compression, - total_weight: 0, - - norm_weight: 0, - data_type: PhantomData, - } - } - - /// Ingests an array of non-NaN f64 values into the TDigest. - pub fn merge_values(&mut self, values: &[T]) { - let centroids = values - .iter() - .map(|val| val.to_float()) - .sorted_by(|a, b| a.partial_cmp(b).unwrap()) - .map(|v| Centroid { mean: v, weight: 1 }) - .collect_vec(); - let compression = self.compression; - let total_weight = centroids.len(); - - // Create an ephemeral TDigest to reuse the same interface. - self.merge(&TDigest { - centroids, - compression, - total_weight, - - norm_weight: 0, - data_type: PhantomData, - }); - } - - /// Merges two TDigests together and returns a new one. - /// Particularly useful for parallel execution. - /// Note: self to_ignore set is *NOT* updated. - pub fn merge(&mut self, other: &TDigest) { - let mut sorted_centroids = self.centroids.iter().merge(other.centroids.iter()); - - let mut new_centroids = Vec::new(); - let total_weight = self.total_weight + other.total_weight; - - // Initialize the greedy merging (copy first Centroid as a starting point). - let mut q_curr = 0.0; - let mut q_limit = self.k_rev_scale(self.k_scale(q_curr) + 1.0); - - let mut tmp_centroid = match sorted_centroids.next() { - Some(centroid) => centroid.clone(), - None => { - return; - } - }; - - // Iterate over ordered and merged Centroids (starting from index 1). - for centroid in sorted_centroids { - let q_new = (tmp_centroid.weight + centroid.weight) as f64 / total_weight as f64; - if (q_curr + q_new) <= q_limit { - tmp_centroid.merge(centroid) - } else { - q_curr += tmp_centroid.weight as f64 / total_weight as f64; - q_limit = self.k_rev_scale(self.k_scale(q_curr) + 1.0); - new_centroids.push(tmp_centroid); - tmp_centroid = centroid.clone(); - } - } - new_centroids.push(tmp_centroid); - - self.centroids = new_centroids; - self.total_weight += other.total_weight; - } - - /// Obtains a given quantile from the TDigest. - /// Returns 0.0 if TDigest is empty. - /// Performs a linear interpollation between two neighboring Centroids if needed. - /// Note: This is *not* normalized with nb_ignored. - pub fn quantile(&self, q: f64) -> f64 { - let target_cum = q * (self.total_weight as f64); - let pos_cum = self // Finds the centroid whose *cumulative weight* exceeds or equals the quantile. - .centroids - .iter() - .map(|c| c.weight) - .scan(0, |acc, weight| { - *acc += weight; - Some(*acc) - }) - .enumerate() - .find(|&(_, cum)| target_cum < (cum as f64)); - - match pos_cum { - Some((pos, cum)) => { - // TODO: We ignore edge-cases where Centroid's weights are 1. - if (pos == 0) || (pos == self.centroids.len() - 1) { - self.centroids[pos].mean - } else { - // Quantile is somewhere between in (prev+curr)/2 and (curr+next)/2 means. - let (prev, curr, next) = ( - &self.centroids[pos - 1], - &self.centroids[pos], - &self.centroids[pos + 1], - ); - let (min_q, max_q) = - ((prev.mean + curr.mean) / 2.0, (curr.mean + next.mean) / 2.0); - lerp( - min_q, - max_q, - ((cum as f64) - target_cum) / (curr.weight as f64), - ) - } - } - None => self.centroids.last().map(|c| c.mean).unwrap_or(0.0), - } - } - - /// Obtains the CDF corresponding to a given value. - /// Returns 0.0 if the TDigest is empty. - /// Note: This *is* normalized with nb_ignored. - pub fn cdf(&self, v: &T) -> f64 { - let mut cum_sum = 0; - let pos_cum = self // Finds the centroid whose *mean* exceeds or equals the given value. - .centroids - .iter() - .enumerate() - .find(|(_, c)| { - cum_sum += c.weight; // Get the cum_sum as a side effect. - v.to_float() < c.mean - }) - .map(|(pos, _)| (pos, cum_sum)); - - let nb_total = self.total_weight as f64; - match pos_cum { - Some((_pos, cum)) => { - // TODO: Can do better with 2 lerps, left as future work. - // TODO: We ignore edge-cases where Centroid's weights are 1. - (cum as f64) / nb_total - } - None => self.centroids.last().map(|_| 1.0).unwrap_or(0.0), - } - } - - // Obtains the k-distance for a given quantile. - // Note: The scaling function implemented is k1 in Ted Dunning's paper. - fn k_scale(&self, quantile: f64) -> f64 { - (self.compression / (2.0 * PI)) * (2.0 * quantile - 1.0).asin() - } - - // Obtains the quantile associated to a k-distance. - // There are probably numerical optimizations to flatten the nested - // k_scale(k_rev_scale()) calls. But let's keep it simple. - fn k_rev_scale(&self, k_distance: f64) -> f64 { - ((2.0 * PI * k_distance / self.compression).sin() + 1.0) / 2.0 - } -} - -// Performs the linear interpolation between a and b, given a fraction f. -fn lerp(a: f64, b: f64, f: f64) -> f64 { - (a * (1.0 - f)) + (b * f) -} - -#[cfg(test)] -mod tests { - use std::sync::{Arc, Mutex}; - - use crossbeam::thread; - use ordered_float::OrderedFloat; - use rand::distributions::{Distribution, Uniform, WeightedIndex}; - use rand::rngs::StdRng; - use rand::SeedableRng; - - use super::{IntoFloat, TDigest}; - - impl IntoFloat for OrderedFloat { - fn to_float(&self) -> f64 { - self.0 - } - } - - // Whether obtained = expected +/- error - fn is_close(obtained: f64, expected: f64, error: f64) -> bool { - ((expected - error) < obtained) && (obtained < (expected + error)) - } - - // Checks whether the tdigest follows a uniform distribution. - fn check_tdigest_uniform( - tdigest: &TDigest>, - buckets: i32, - max: f64, - min: f64, - error: f64, - ) { - for k in 0..buckets { - let expected_cdf = (k as f64) / (buckets as f64); - let expected_quantile = (max - min) * expected_cdf + min; - - let obtained_cdf = tdigest.cdf(&OrderedFloat(expected_quantile)); - let obtained_quantile = tdigest.quantile(expected_cdf); - - assert!(is_close(obtained_cdf, expected_cdf, error)); - assert!(is_close( - obtained_quantile, - expected_quantile, - (max - min) * error, - )); - } - } - - #[test] - fn uniform_merge_sequential() { - let buckets = 200; - let error = 0.03; // 3% absolute error on each quantile; error gets worse near the median. - let mut tdigest = TDigest::new(buckets as f64); - - let (min, max) = (-1000.0, 1000.0); - let uniform_distr = Uniform::new(min, max); - let mut rng = StdRng::seed_from_u64(0); - - let batch_size = 1024; - let batch_numbers = 64; - - for _ in 0..batch_numbers { - let mut random_numbers = Vec::with_capacity(batch_size); - for _ in 0..batch_size { - let num: f64 = uniform_distr.sample(&mut rng); - random_numbers.push(OrderedFloat(num)); - } - tdigest.merge_values(&random_numbers); - } - - check_tdigest_uniform(&tdigest, buckets, max, min, error); - } - - #[test] - fn uniform_merge_parallel() { - let buckets = 200; - let error = 0.03; // 3% absolute error on each quantile, note error is worse near the median. - - let (min, max) = (-1000.0, 1000.0); - - let batch_size = 65536; - let batch_numbers = 64; - - let result_tdigest = Arc::new(Mutex::new(TDigest::new(buckets as f64))); - thread::scope(|s| { - for _ in 0..batch_numbers { - s.spawn(|_| { - let mut local_tdigest = TDigest::new(buckets as f64); - - let mut random_numbers = Vec::with_capacity(batch_size); - let uniform_distr = Uniform::new(min, max); - let mut rng = StdRng::seed_from_u64(0); - - for _ in 0..batch_size { - let num: f64 = uniform_distr.sample(&mut rng); - random_numbers.push(OrderedFloat(num)); - } - local_tdigest.merge_values(&random_numbers); - - let mut result = result_tdigest.lock().unwrap(); - result.merge(&local_tdigest); - }); - } - }) - .unwrap(); - - let tdigest = result_tdigest.lock().unwrap(); - check_tdigest_uniform(&tdigest, buckets, max, min, error); - } - - #[test] - fn weighted_merge() { - let buckets = 200; - let error = 0.05; // 5% absolute error on each quantile, note error is worse near the median. - - let mut tdigest = TDigest::new(buckets as f64); - - let choices = [9.0, 900.0, 990.0, 9990.0, 190000.0, 990000.0]; - let weights = [1, 2, 1, 3, 4, 5]; // Total of 16. - let total_weight: i32 = weights.iter().sum(); - - let weighted_distr = WeightedIndex::new(weights).unwrap(); - let mut rng = StdRng::seed_from_u64(0); - - let batch_size = 128; - let batch_numbers = 16; - - for _ in 0..batch_numbers { - let mut random_numbers = Vec::with_capacity(batch_size); - for _ in 0..batch_size { - let num: f64 = choices[weighted_distr.sample(&mut rng)]; - random_numbers.push(OrderedFloat(num)); - } - tdigest.merge_values(&random_numbers); - } - - let mut curr_weight = 0; - for (c, w) in choices.iter().zip(weights) { - curr_weight += w; - let estimate_cdf = tdigest.cdf(&OrderedFloat(*c)); - let obtained_cdf = (curr_weight as f64) / (total_weight as f64); - assert!(is_close(obtained_cdf, estimate_cdf, error)); - } - } -} diff --git a/optd-cost-model/src/storage/mock.rs b/optd-cost-model/src/storage/mock.rs deleted file mode 100644 index f20c417..0000000 --- a/optd-cost-model/src/storage/mock.rs +++ /dev/null @@ -1,89 +0,0 @@ -#![allow(unused_variables, dead_code)] -use std::collections::HashMap; - -use serde::{Deserialize, Serialize}; - -use crate::{ - common::types::{EpochId, ExprId, TableId}, - stats::AttributeCombValueStats, - Cost, CostModelResult, EstimatedStatistic, -}; - -use super::CostModelStorageManager; - -pub type AttrIndices = Vec; - -#[serde_with::serde_as] -#[derive(Serialize, Deserialize, Debug)] -pub struct TableStats { - pub row_cnt: u64, - #[serde_as(as = "HashMap")] - pub column_comb_stats: HashMap, -} - -impl TableStats { - pub fn new( - row_cnt: u64, - column_comb_stats: HashMap, - ) -> Self { - Self { - row_cnt, - column_comb_stats, - } - } -} - -pub type BaseTableStats = HashMap; - -pub struct CostModelStorageMockManagerImpl { - pub(crate) per_table_stats_map: BaseTableStats, -} - -impl CostModelStorageMockManagerImpl { - pub fn new(per_table_stats_map: BaseTableStats) -> Self { - Self { - per_table_stats_map, - } - } -} - -impl CostModelStorageManager for CostModelStorageMockManagerImpl { - async fn get_attributes_comb_statistics( - &self, - table_id: TableId, - attr_base_indices: &[u64], - ) -> CostModelResult> { - let table_stats = self.per_table_stats_map.get(&table_id); - match table_stats { - None => Ok(None), - Some(table_stats) => match table_stats.column_comb_stats.get(attr_base_indices) { - None => Ok(None), - Some(stats) => Ok(Some(stats.clone())), - }, - } - } - - async fn get_table_row_count(&self, table_id: TableId) -> CostModelResult> { - let table_stats = self.per_table_stats_map.get(&table_id); - Ok(table_stats.map(|stats| stats.row_cnt)) - } - - /// TODO: finish this when implementing the cost get/store tests - async fn get_cost( - &self, - expr_id: ExprId, - ) -> CostModelResult<(Option, Option)> { - todo!() - } - - /// TODO: finish this when implementing the cost get/store tests - async fn store_cost( - &self, - expr_id: ExprId, - cost: Option, - estimated_statistic: Option, - epoch_id: Option, - ) -> CostModelResult<()> { - todo!() - } -} diff --git a/optd-cost-model/src/storage/mod.rs b/optd-cost-model/src/storage/mod.rs deleted file mode 100644 index 14cccd6..0000000 --- a/optd-cost-model/src/storage/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::{ - common::types::{EpochId, ExprId, TableId}, - stats::AttributeCombValueStats, - Cost, CostModelResult, EstimatedStatistic, -}; - -pub mod mock; -pub mod persistent; - -#[trait_variant::make(Send)] -pub trait CostModelStorageManager { - async fn get_attributes_comb_statistics( - &self, - table_id: TableId, - attr_base_indices: &[u64], - ) -> CostModelResult>; - - async fn get_table_row_count(&self, table_id: TableId) -> CostModelResult>; - - async fn get_cost( - &self, - expr_id: ExprId, - ) -> CostModelResult<(Option, Option)>; - - async fn store_cost( - &self, - expr_id: ExprId, - cost: Option, - estimated_statistic: Option, - epoch_id: Option, - ) -> CostModelResult<()>; -} diff --git a/optd-cost-model/src/storage/persistent.rs b/optd-cost-model/src/storage/persistent.rs deleted file mode 100644 index b3078a6..0000000 --- a/optd-cost-model/src/storage/persistent.rs +++ /dev/null @@ -1,166 +0,0 @@ -#![allow(unused_variables)] -use std::sync::Arc; - -use optd_persistent::{cost_model::interface::StatType, CostModelStorageLayer}; - -use crate::{ - common::types::{EpochId, ExprId, TableId}, - stats::{utilities::counter::Counter, AttributeCombValueStats, Distribution, MostCommonValues}, - Cost, CostModelResult, EstimatedStatistic, -}; - -use super::CostModelStorageManager; - -/// TODO: documentation -pub struct CostModelStorageManagerImpl { - pub backend_manager: Arc, - // TODO: in-memory cache -} - -impl CostModelStorageManagerImpl { - pub fn new(backend_manager: Arc) -> Self { - Self { backend_manager } - } -} - -impl CostModelStorageManager - for CostModelStorageManagerImpl -{ - /// Gets the latest statistics for a given table. Currently we only support base table - /// statistic retrieval. - /// - /// TODO: Currently, in `AttributeCombValueStats`, only `Distribution` is optional. - /// This poses a question about the behavior of the system if there is no corresponding - /// `MostCommonValues`, `ndistinct`, or other statistics. We should have a clear - /// specification about the behavior of the system in the presence of missing statistics. - /// - /// TODO: if we have memory cache, - /// we should add the reference. (&AttributeCombValueStats) - /// - /// TODO: Shall we pass in an epoch here to make sure that the statistics are from the same - /// epoch? - async fn get_attributes_comb_statistics( - &self, - table_id: TableId, - attr_base_indices: &[u64], - ) -> CostModelResult> { - let dist: Option = self - .backend_manager - .get_stats_for_attr_indices_based( - table_id.into(), - attr_base_indices.iter().map(|&x| x as i32).collect(), - StatType::Distribution, - None, - ) - .await? - .map(|json| serde_json::from_value(json).unwrap()); - - let mcvs = self - .backend_manager - .get_stats_for_attr_indices_based( - table_id.into(), - attr_base_indices.iter().map(|&x| x as i32).collect(), - StatType::MostCommonValues, - None, - ) - .await? - .map(|json| serde_json::from_value(json).unwrap()) - .unwrap_or_else(|| MostCommonValues::Counter(Counter::default())); - - let ndistinct = self - .backend_manager - .get_stats_for_attr_indices_based( - table_id.into(), - attr_base_indices.iter().map(|&x| x as i32).collect(), - StatType::Cardinality, - None, - ) - .await? - .map(|json| serde_json::from_value(json).unwrap()) - .unwrap_or(0); - - let table_row_count = self - .backend_manager - .get_stats_for_attr_indices_based( - table_id.into(), - attr_base_indices.iter().map(|&x| x as i32).collect(), - StatType::TableRowCount, - None, - ) - .await? - .map(|json| serde_json::from_value(json).unwrap()) - .unwrap_or(0); - let non_null_count = self - .backend_manager - .get_stats_for_attr_indices_based( - table_id.into(), - attr_base_indices.iter().map(|&x| x as i32).collect(), - StatType::NonNullCount, - None, - ) - .await? - .map(|json| serde_json::from_value(json).unwrap()) - .unwrap_or(0); - - // FIXME: Only minimal checks for invalid values is conducted here. We should have - // much clear specification about the behavior of the system in the presence of - // invalid statistics. - let null_frac = if table_row_count == 0 { - 0.0 - } else { - 1.0 - (non_null_count as f64 / table_row_count as f64) - }; - - Ok(Some(AttributeCombValueStats::new( - mcvs, dist, ndistinct, null_frac, - ))) - } - - async fn get_table_row_count(&self, table_id: TableId) -> CostModelResult> { - Ok(self - .backend_manager - .get_stats_for_table(table_id.into(), StatType::TableRowCount, None) - .await? - .map(serde_json::from_value) - .transpose()?) - } - - /// TODO: The name is misleading, since we can also get the estimated statistic. We should - /// rename it. - /// - /// TODO: Add retry logic here. - async fn get_cost( - &self, - expr_id: ExprId, - ) -> CostModelResult<(Option, Option)> { - let (cost, estimated_statistic) = self.backend_manager.get_cost(expr_id.into()).await?; - Ok(( - cost.map(|c| c.into()), - estimated_statistic.map(|x| x.into()), - )) - } - - /// TODO: The name is misleading, since we can also get the estimated statistic. We should - /// rename it. - /// - /// TODO: Add retry logic here. - async fn store_cost( - &self, - expr_id: ExprId, - cost: Option, - estimated_statistic: Option, - epoch_id: Option, - ) -> CostModelResult<()> { - self.backend_manager - .store_cost( - expr_id.into(), - cost.map(|c| c.into()), - estimated_statistic.map(|x| x.into()), - epoch_id.map(|id| id.into()), - ) - .await?; - Ok(()) - } - - // TODO: Support querying for a specific type of statistics. -} diff --git a/optd-cost-model/src/test_utils.rs b/optd-cost-model/src/test_utils.rs deleted file mode 100644 index 60db90f..0000000 --- a/optd-cost-model/src/test_utils.rs +++ /dev/null @@ -1,565 +0,0 @@ -/// I thought about using the system's own parser and planner to generate these expression trees, -/// but this is not currently feasible because it would create a cyclic dependency between -/// optd-datafusion-bridge and optd-datafusion-repr -#[cfg(test)] -pub mod tests { - use itertools::Itertools; - use std::{collections::HashMap, sync::Arc}; - - use arrow_schema::DataType; - use optd_persistent::cost_model::interface::CatalogSource; - - use crate::{ - common::{ - nodes::{ArcPredicateNode, ReprPredicateNode}, - predicates::{ - attr_index_pred::AttrIndexPred, - bin_op_pred::{BinOpPred, BinOpType}, - cast_pred::CastPred, - constant_pred::{ConstantPred, ConstantType}, - in_list_pred::InListPred, - like_pred::LikePred, - list_pred::ListPred, - log_op_pred::{LogOpPred, LogOpType}, - un_op_pred::{UnOpPred, UnOpType}, - }, - properties::{ - attr_ref::{AttrRef, GroupAttrRefs}, - schema::Schema, - Attribute, - }, - types::{GroupId, TableId}, - values::Value, - }, - cost_model::CostModelImpl, - memo_ext::MemoExt, - stats::{ - utilities::simple_map::SimpleMap, AttributeCombValueStats, Distribution, - MostCommonValues, - }, - storage::mock::{CostModelStorageMockManagerImpl, TableStats}, - }; - - pub struct MemoGroupInfo { - pub schema: Schema, - pub attr_refs: GroupAttrRefs, - } - - impl MemoGroupInfo { - pub fn new(schema: Schema, attr_refs: GroupAttrRefs) -> Self { - Self { schema, attr_refs } - } - } - - #[derive(Default)] - pub struct MockMemoExtImpl { - memo: HashMap, - } - - impl MockMemoExtImpl { - pub fn add_group_info( - &mut self, - group_id: GroupId, - schema: Schema, - attr_ref: GroupAttrRefs, - ) { - self.memo - .insert(group_id, MemoGroupInfo::new(schema, attr_ref)); - } - } - - impl MemoExt for MockMemoExtImpl { - fn get_schema(&self, group_id: GroupId) -> Schema { - self.memo.get(&group_id).unwrap().schema.clone() - } - - fn get_attribute_info(&self, group_id: GroupId, attr_ref_idx: u64) -> Attribute { - self.memo.get(&group_id).unwrap().schema.attributes[attr_ref_idx as usize].clone() - } - - fn get_attribute_refs(&self, group_id: GroupId) -> GroupAttrRefs { - self.memo.get(&group_id).unwrap().attr_refs.clone() - } - - fn get_attribute_ref(&self, group_id: GroupId, attr_ref_idx: u64) -> AttrRef { - self.memo.get(&group_id).unwrap().attr_refs.attr_refs()[attr_ref_idx as usize].clone() - } - } - - impl From> for MockMemoExtImpl { - fn from(memo: HashMap) -> Self { - Self { memo } - } - } - - pub const TEST_TABLE1_ID: TableId = TableId(0); - pub const TEST_TABLE2_ID: TableId = TableId(1); - pub const TEST_TABLE3_ID: TableId = TableId(2); - pub const TEST_TABLE4_ID: TableId = TableId(3); - - pub const TEST_GROUP1_ID: GroupId = GroupId(0); - pub const TEST_GROUP2_ID: GroupId = GroupId(1); - pub const TEST_GROUP3_ID: GroupId = GroupId(2); - pub const TEST_GROUP4_ID: GroupId = GroupId(3); - - // This is base index rather than ref index. - pub const TEST_ATTR1_BASE_INDEX: u64 = 0; - pub const TEST_ATTR2_BASE_INDEX: u64 = 1; - pub const TEST_ATTR3_BASE_INDEX: u64 = 2; - - pub const TEST_ATTR1_NAME: &str = "attr1"; - pub const TEST_ATTR2_NAME: &str = "attr2"; - pub const TEST_ATTR3_NAME: &str = "attr3"; - pub const TEST_ATTR4_NAME: &str = "attr4"; - - pub type TestPerAttributeStats = AttributeCombValueStats; - // TODO: add tests for non-mock storage manager - pub type TestOptCostModelMock = CostModelImpl; - - // Use this method, we only create one group `TEST_GROUP1_ID` in the memo. - // We put the first attribute in the first table as the ref index 0 in the group. - // And put the second attribute in the first table as the ref index 1 in the group. - // etc. - // The orders of attributes and tables are defined by the order of their ids (smaller first). - pub fn create_mock_cost_model( - table_id: Vec, - // u64 should be base attribute index. - per_attribute_stats: Vec>, - row_counts: Vec>, - ) -> TestOptCostModelMock { - let attr_ids: Vec<(TableId, u64, Option)> = per_attribute_stats - .iter() - .enumerate() - .map(|(idx, m)| (table_id[idx], m)) - .flat_map(|(table_id, m)| { - m.iter() - .map(|(attr_idx, _)| (table_id, *attr_idx, None)) - .collect_vec() - }) - .sorted_by_key(|(table_id, attr_idx, _)| (*table_id, *attr_idx)) - .collect(); - create_mock_cost_model_with_memo( - table_id.clone(), - per_attribute_stats, - row_counts, - create_one_group_all_base_attributes_mock_memo(attr_ids), - ) - } - - pub fn create_mock_cost_model_with_attr_types( - table_id: Vec, - // u64 should be base attribute index. - per_attribute_stats: Vec>, - attributes: Vec>, - row_counts: Vec>, - ) -> TestOptCostModelMock { - let attr_ids: Vec<(TableId, u64, Option)> = attributes - .iter() - .enumerate() - .map(|(idx, m)| (table_id[idx], m)) - .flat_map(|(table_id, m)| { - m.iter() - .map(|(attr_idx, typ)| (table_id, *attr_idx, Some(*typ))) - .collect_vec() - }) - .sorted_by_key(|(table_id, attr_idx, _)| (*table_id, *attr_idx)) - .collect(); - create_mock_cost_model_with_memo( - table_id.clone(), - per_attribute_stats, - row_counts, - create_one_group_all_base_attributes_mock_memo(attr_ids), - ) - } - - pub fn create_mock_cost_model_with_memo( - table_id: Vec, - per_attribute_stats: Vec>, - row_counts: Vec>, - memo: MockMemoExtImpl, - ) -> TestOptCostModelMock { - let storage_manager = CostModelStorageMockManagerImpl::new( - table_id - .into_iter() - .zip(per_attribute_stats) - .zip(row_counts) - .map(|((table_id, per_attr_stats), row_count)| { - ( - table_id, - TableStats::new( - row_count.unwrap_or(100), - per_attr_stats - .into_iter() - .map(|(attr_idx, stats)| (vec![attr_idx], stats)) - .collect(), - ), - ) - }) - .collect(), - ); - CostModelImpl::new(storage_manager, CatalogSource::Mock, Arc::new(memo)) - } - - // attributes: Vec<(TableId, AttrBaseIndex)> - pub fn create_one_group_all_base_attributes_mock_memo( - attr_ids: Vec<(TableId, u64, Option)>, - ) -> MockMemoExtImpl { - let group_info = MemoGroupInfo::new( - Schema::new( - attr_ids - .clone() - .into_iter() - .map(|(_, _, typ)| Attribute { - name: "attr".to_string(), - typ: typ.unwrap_or(ConstantType::Int64), - nullable: false, - }) - .collect(), - ), - GroupAttrRefs::new( - attr_ids - .into_iter() - .map(|(table_id, attr_base_index, _)| { - AttrRef::new_base_table_attr_ref(table_id, attr_base_index) - }) - .collect(), - None, - ), - ); - MockMemoExtImpl::from(HashMap::from([(TEST_GROUP1_ID, group_info)])) - } - - /// Create a cost model two tables, each with one attribute. Each attribute has 100 values. - pub fn create_two_table_mock_cost_model( - tbl1_per_attr_stats: TestPerAttributeStats, - tbl2_per_attr_stats: TestPerAttributeStats, - additional_memo: Option>, - ) -> TestOptCostModelMock { - create_two_table_mock_cost_model_custom_row_cnts( - tbl1_per_attr_stats, - tbl2_per_attr_stats, - 100, - 100, - additional_memo, - ) - } - - /// Create a cost model three tables, each with one attribute. Each attribute has 100 values. - pub fn create_three_table_mock_cost_model( - tbl1_per_column_stats: TestPerAttributeStats, - tbl2_per_column_stats: TestPerAttributeStats, - tbl3_per_column_stats: TestPerAttributeStats, - ) -> TestOptCostModelMock { - let storage_manager = CostModelStorageMockManagerImpl::new( - vec![ - ( - TEST_TABLE1_ID, - TableStats::new( - 100, - vec![(vec![0], tbl1_per_column_stats)].into_iter().collect(), - ), - ), - ( - TEST_TABLE2_ID, - TableStats::new( - 100, - vec![(vec![0], tbl2_per_column_stats)].into_iter().collect(), - ), - ), - ( - TEST_TABLE3_ID, - TableStats::new( - 100, - vec![(vec![0], tbl3_per_column_stats)].into_iter().collect(), - ), - ), - ] - .into_iter() - .collect(), - ); - let memo = HashMap::from([ - ( - TEST_GROUP1_ID, - MemoGroupInfo::new( - vec![Attribute::new_non_null_int64(TEST_ATTR1_NAME.to_string())].into(), - GroupAttrRefs::new( - vec![AttrRef::new_base_table_attr_ref(TEST_TABLE1_ID, 0)], - None, - ), - ), - ), - ( - TEST_GROUP2_ID, - MemoGroupInfo::new( - vec![Attribute::new_non_null_int64(TEST_ATTR2_NAME.to_string())].into(), - GroupAttrRefs::new( - vec![AttrRef::new_base_table_attr_ref(TEST_TABLE2_ID, 0)], - None, - ), - ), - ), - ( - TEST_GROUP3_ID, - MemoGroupInfo::new( - vec![Attribute::new_non_null_int64(TEST_ATTR3_NAME.to_string())].into(), - GroupAttrRefs::new( - vec![AttrRef::new_base_table_attr_ref(TEST_TABLE3_ID, 0)], - None, - ), - ), - ), - ]); - CostModelImpl::new( - storage_manager, - CatalogSource::Mock, - Arc::new(MockMemoExtImpl::from(memo)), - ) - } - - /// Create a cost model four tables, each with one attribute. Each attribute has 100 values. - pub fn create_four_table_mock_cost_model( - tbl1_per_column_stats: TestPerAttributeStats, - tbl2_per_column_stats: TestPerAttributeStats, - tbl3_per_column_stats: TestPerAttributeStats, - tbl4_per_column_stats: TestPerAttributeStats, - ) -> TestOptCostModelMock { - let storage_manager = CostModelStorageMockManagerImpl::new( - vec![ - ( - TEST_TABLE1_ID, - TableStats::new( - 100, - vec![(vec![0], tbl1_per_column_stats)].into_iter().collect(), - ), - ), - ( - TEST_TABLE2_ID, - TableStats::new( - 100, - vec![(vec![0], tbl2_per_column_stats)].into_iter().collect(), - ), - ), - ( - TEST_TABLE3_ID, - TableStats::new( - 100, - vec![(vec![0], tbl3_per_column_stats)].into_iter().collect(), - ), - ), - ( - TEST_TABLE4_ID, - TableStats::new( - 100, - vec![(vec![0], tbl4_per_column_stats)].into_iter().collect(), - ), - ), - ] - .into_iter() - .collect(), - ); - let memo = HashMap::from([ - ( - TEST_GROUP1_ID, - MemoGroupInfo::new( - vec![Attribute::new_non_null_int64(TEST_ATTR1_NAME.to_string())].into(), - GroupAttrRefs::new( - vec![AttrRef::new_base_table_attr_ref(TEST_TABLE1_ID, 0)], - None, - ), - ), - ), - ( - TEST_GROUP2_ID, - MemoGroupInfo::new( - vec![Attribute::new_non_null_int64(TEST_ATTR2_NAME.to_string())].into(), - GroupAttrRefs::new( - vec![AttrRef::new_base_table_attr_ref(TEST_TABLE2_ID, 0)], - None, - ), - ), - ), - ( - TEST_GROUP3_ID, - MemoGroupInfo::new( - vec![Attribute::new_non_null_int64(TEST_ATTR3_NAME.to_string())].into(), - GroupAttrRefs::new( - vec![AttrRef::new_base_table_attr_ref(TEST_TABLE3_ID, 0)], - None, - ), - ), - ), - ( - TEST_GROUP4_ID, - MemoGroupInfo::new( - vec![Attribute::new_non_null_int64(TEST_ATTR4_NAME.to_string())].into(), - GroupAttrRefs::new( - vec![AttrRef::new_base_table_attr_ref(TEST_TABLE4_ID, 0)], - None, - ), - ), - ), - ]); - CostModelImpl::new( - storage_manager, - CatalogSource::Mock, - Arc::new(MockMemoExtImpl::from(memo)), - ) - } - - /// We need custom row counts because some join algorithms rely on the row cnt - pub fn create_two_table_mock_cost_model_custom_row_cnts( - tbl1_per_column_stats: TestPerAttributeStats, - tbl2_per_column_stats: TestPerAttributeStats, - tbl1_row_cnt: u64, - tbl2_row_cnt: u64, - additional_memo: Option>, - ) -> TestOptCostModelMock { - let storage_manager = CostModelStorageMockManagerImpl::new( - vec![ - ( - TEST_TABLE1_ID, - TableStats::new( - tbl1_row_cnt, - vec![(vec![0], tbl1_per_column_stats)].into_iter().collect(), - ), - ), - ( - TEST_TABLE2_ID, - TableStats::new( - tbl2_row_cnt, - vec![(vec![0], tbl2_per_column_stats)].into_iter().collect(), - ), - ), - ] - .into_iter() - .collect(), - ); - let mut memo = HashMap::from([ - ( - TEST_GROUP1_ID, - MemoGroupInfo::new( - vec![Attribute::new_non_null_int64(TEST_ATTR1_NAME.to_string())].into(), - GroupAttrRefs::new( - vec![AttrRef::new_base_table_attr_ref(TEST_TABLE1_ID, 0)], - None, - ), - ), - ), - ( - TEST_GROUP2_ID, - MemoGroupInfo::new( - vec![Attribute::new_non_null_int64(TEST_ATTR2_NAME.to_string())].into(), - GroupAttrRefs::new( - vec![AttrRef::new_base_table_attr_ref(TEST_TABLE2_ID, 0)], - None, - ), - ), - ), - ]); - if let Some(additional_memo) = additional_memo { - memo.extend(additional_memo); - } - CostModelImpl::new( - storage_manager, - CatalogSource::Mock, - Arc::new(MockMemoExtImpl::from(memo)), - ) - } - - impl TestOptCostModelMock { - pub fn get_row_count(&self, table_id: TableId) -> u64 { - self.storage_manager - .per_table_stats_map - .get(&table_id) - .map(|stats| stats.row_cnt) - .unwrap_or(0) - } - - pub fn get_attr_refs(&self, group_id: GroupId) -> GroupAttrRefs { - self.memo.get_attribute_refs(group_id) - } - } - - pub fn attr_index(attr_index: u64) -> ArcPredicateNode { - AttrIndexPred::new(attr_index).into_pred_node() - } - - pub fn cnst(value: Value) -> ArcPredicateNode { - ConstantPred::new(value).into_pred_node() - } - - pub fn cast(child: ArcPredicateNode, cast_type: DataType) -> ArcPredicateNode { - CastPred::new(child, cast_type).into_pred_node() - } - - pub fn bin_op( - op_type: BinOpType, - left: ArcPredicateNode, - right: ArcPredicateNode, - ) -> ArcPredicateNode { - BinOpPred::new(left, right, op_type).into_pred_node() - } - - pub fn log_op(op_type: LogOpType, children: Vec) -> ArcPredicateNode { - LogOpPred::new(op_type, children).into_pred_node() - } - - pub fn un_op(op_type: UnOpType, child: ArcPredicateNode) -> ArcPredicateNode { - UnOpPred::new(child, op_type).into_pred_node() - } - - pub fn empty_list() -> ArcPredicateNode { - ListPred::new(vec![]).into_pred_node() - } - - pub fn list(children: Vec) -> ArcPredicateNode { - ListPred::new(children).into_pred_node() - } - - pub fn in_list(attr_idx: u64, list: Vec, negated: bool) -> InListPred { - InListPred::new( - attr_index(attr_idx), - ListPred::new(list.into_iter().map(cnst).collect_vec()), - negated, - ) - } - - pub fn like(attr_idx: u64, pattern: &str, negated: bool) -> LikePred { - LikePred::new( - negated, - false, - attr_index(attr_idx), - cnst(Value::String(pattern.into())), - ) - } - - pub(crate) fn empty_per_attr_stats() -> TestPerAttributeStats { - TestPerAttributeStats::new( - MostCommonValues::empty(), - Some(Distribution::empty()), - 0, - 0.0, - ) - } - - pub(crate) fn per_attr_stats_with_ndistinct(ndistinct: u64) -> TestPerAttributeStats { - TestPerAttributeStats::new( - MostCommonValues::empty(), - Some(Distribution::empty()), - ndistinct, - 0.0, - ) - } - - pub(crate) fn per_attr_stats_with_dist_and_ndistinct( - dist: Vec<(Value, f64)>, - ndistinct: u64, - ) -> TestPerAttributeStats { - TestPerAttributeStats::new( - MostCommonValues::empty(), - Some(Distribution::SimpleDistribution(SimpleMap::new(dist))), - ndistinct, - 0.0, - ) - } -} diff --git a/optd-cost-model/src/utils.rs b/optd-cost-model/src/utils.rs deleted file mode 100644 index 125c499..0000000 --- a/optd-cost-model/src/utils.rs +++ /dev/null @@ -1,118 +0,0 @@ -//! optd's implementation of disjoint sets (union finds). It's send + sync + serializable. - -use std::{collections::HashMap, hash::Hash}; -#[derive(Clone, Default)] -pub struct DisjointSets { - data_idx: HashMap, - parents: Vec, -} - -impl std::fmt::Debug for DisjointSets { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "DisjointSets") - } -} - -impl DisjointSets { - pub fn new() -> Self { - Self { - data_idx: HashMap::new(), - parents: Vec::new(), - } - } - - pub fn contains(&self, data: &T) -> bool { - self.data_idx.contains_key(data) - } - - #[must_use] - pub fn make_set(&mut self, data: T) -> Option<()> { - if self.data_idx.contains_key(&data) { - return None; - } - let idx = self.parents.len(); - self.data_idx.insert(data.clone(), idx); - self.parents.push(idx); - Some(()) - } - - fn find(&mut self, mut idx: usize) -> usize { - while self.parents[idx] != idx { - self.parents[idx] = self.parents[self.parents[idx]]; - idx = self.parents[idx]; - } - idx - } - - fn find_const(&self, mut idx: usize) -> usize { - while self.parents[idx] != idx { - idx = self.parents[idx]; - } - idx - } - - #[must_use] - pub fn union(&mut self, data1: &T, data2: &T) -> Option<()> { - let idx1 = *self.data_idx.get(data1)?; - let idx2 = *self.data_idx.get(data2)?; - let parent1 = self.find(idx1); - let parent2 = self.find(idx2); - if parent1 != parent2 { - self.parents[parent1] = parent2; - } - Some(()) - } - - pub fn same_set(&self, data1: &T, data2: &T) -> Option { - let idx1 = *self.data_idx.get(data1)?; - let idx2 = *self.data_idx.get(data2)?; - Some(self.find_const(idx1) == self.find_const(idx2)) - } - - pub fn set_size(&self, data: &T) -> Option { - let idx = *self.data_idx.get(data)?; - let parent = self.find_const(idx); - Some( - self.parents - .iter() - .filter(|&&x| self.find_const(x) == parent) - .count(), - ) - } -} - -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn test_union_find() { - let mut set = DisjointSets::new(); - set.make_set("a").unwrap(); - set.make_set("b").unwrap(); - set.make_set("c").unwrap(); - set.make_set("d").unwrap(); - set.make_set("e").unwrap(); - assert!(set.same_set(&"a", &"a").unwrap()); - assert!(!set.same_set(&"a", &"b").unwrap()); - assert_eq!(set.set_size(&"a").unwrap(), 1); - assert_eq!(set.set_size(&"c").unwrap(), 1); - set.union(&"a", &"b").unwrap(); - assert_eq!(set.set_size(&"a").unwrap(), 2); - assert_eq!(set.set_size(&"c").unwrap(), 1); - assert!(set.same_set(&"a", &"b").unwrap()); - assert!(!set.same_set(&"a", &"c").unwrap()); - set.union(&"b", &"c").unwrap(); - assert!(set.same_set(&"a", &"c").unwrap()); - assert!(!set.same_set(&"a", &"d").unwrap()); - assert_eq!(set.set_size(&"a").unwrap(), 3); - assert_eq!(set.set_size(&"d").unwrap(), 1); - set.union(&"d", &"e").unwrap(); - assert!(set.same_set(&"d", &"e").unwrap()); - assert!(!set.same_set(&"a", &"d").unwrap()); - assert_eq!(set.set_size(&"a").unwrap(), 3); - assert_eq!(set.set_size(&"d").unwrap(), 2); - set.union(&"c", &"e").unwrap(); - assert!(set.same_set(&"a", &"e").unwrap()); - assert_eq!(set.set_size(&"d").unwrap(), 5); - } -} diff --git a/optd-persistent/Cargo.lock b/optd-persistent/Cargo.lock deleted file mode 100644 index 1c45426..0000000 --- a/optd-persistent/Cargo.lock +++ /dev/null @@ -1,2814 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "addr2line" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aa100a6f6f525226719f8de3f70076be4f4191801ebd92621450d1c51e9053d" - -[[package]] -name = "ahash" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aliasable" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" - -[[package]] -name = "allocator-api2" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52f4a9cf8f3ff707b4eb1acd0136efd8b3bec6b345ed32fcab47c0a5c99b800" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anstream" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is-terminal", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" - -[[package]] -name = "anstyle-parse" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "anstyle-wincon" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd" -dependencies = [ - "anstyle", - "windows-sys 0.48.0", -] - -[[package]] -name = "arrayvec" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a2f58b0bb10c380af2b26e57212856b8c9a59e0925b4c20f4a174a49734eaf7" - -[[package]] -name = "async-stream" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a26cb53174ddd320edfff199a853f93d571f48eeb4dde75e67a9a3dbb7b7e5e" -dependencies = [ - "async-stream-impl", - "futures-core", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db134ba52475c060f3329a8ef0f8786d6b872ed01515d4b79c162e5798da1340" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "async-trait" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6287685011f026b98d26afd53251ad0101e856531b423eb2384265f7d4f5b01" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "atoi" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" -dependencies = [ - "num-traits", -] - -[[package]] -name = "autocfg" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" - -[[package]] -name = "backtrace" -version = "0.3.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88fb5a785d6b44fd9d6700935608639af1b8356de1e55d5f7c2740f4faa15d82" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" - -[[package]] -name = "base64" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" - -[[package]] -name = "base64ct" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71acf5509fc522cce1b100ac0121c635129bfd4d91cdf036bcc9b9935f97ccf5" - -[[package]] -name = "bigdecimal" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5274a6b6e0ee020148397245b973e30163b7bffbc6d473613f850cb99888581e" -dependencies = [ - "libm", - "num-bigint", - "num-integer", - "num-traits", - "serde", -] - -[[package]] -name = "bitflags" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" - -[[package]] -name = "bitflags" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" -dependencies = [ - "serde", -] - -[[package]] -name = "block-buffer" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03588e54c62ae6d763e2a80090d50353b785795361b4ff5b3bf0a5097fc31c0b" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bumpalo" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" - -[[package]] -name = "cc" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "num-traits", - "serde", - "windows-targets 0.52.0", -] - -[[package]] -name = "clap" -version = "4.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384e169cc618c613d5e3ca6404dda77a8685a63e08660dcc64abaf7da7cb0c7a" -dependencies = [ - "clap_builder", - "clap_derive", - "once_cell", -] - -[[package]] -name = "clap_builder" -version = "4.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef137bbe35aab78bdb468ccfba75a5f4d8321ae011d34063770780545176af2d" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "clap_lex" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" - -[[package]] -name = "codespan-reporting" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ce42b8998a383572e0a802d859b1f00c79b7b7474e62fff88ee5c2845d9c13" -dependencies = [ - "termcolor", - "unicode-width", -] - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - -[[package]] -name = "concurrent-queue" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "const-oid" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "crc" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff" - -[[package]] -name = "crossbeam-queue" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "crypto-common" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "cxx" -version = "1.0.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c53d75fe543215ca091d792e13351dcb940842dd2829b2a2dd43ab4bd1a015" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "618f85c8f132bd8912aab124e15a38adc762bb7e3cef84524adde1692ef3e8bc" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 1.0.98", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca21461be76a23df4f63a2107a0bb406ef41548e635ff7edcbd1ab5a6bb997e2" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8da0a2c0697647b5824844a5d2dedcd97a2d7b75e6e4d0b8dd183e4081e1cf" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "darling" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c99d16b88c92aef47e58dadd53e87b4bd234c29934947a6cec8b466300f99b" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ea05d2fcb27b53f7a98faddaf5f2914760330ab7703adfc9df13332b42189f9" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "darling_macro" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bfb82b62b1b8a2a9808fb4caf844ede819a76cfc23b2827d7f94eefb49551eb" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "der" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19c5cb402c5c958281c7c0702edea7b780d03b86b606497ca3a10fcd3fc393ac" -dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" -dependencies = [ - "powerfmt", - "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 = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dotenvy" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9155c8f4dc55c7470ae9da3f63c6785245093b3f6aeb0f5bf2e968efbba314" -dependencies = [ - "dirs", -] - -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" -dependencies = [ - "serde", -] - -[[package]] -name = "equivalent" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" - -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "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 = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "fastrand" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" - -[[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 0.9.8", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" -dependencies = [ - "matches", - "percent-encoding", -] - -[[package]] -name = "futures" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fa4cc29d25b0687b8570b0da86eac698dcb525110ad8b938fe6712baa711ec" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a" -dependencies = [ - "futures-core", - "futures-task", - "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.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" - -[[package]] -name = "glob" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - -[[package]] -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.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692eaaf7f7607518dd3cef090f1474b61edc5301d8012f09579920df68b725ee" -dependencies = [ - "hashbrown", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hkdf" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f41e9c77b6fc05b57497b960aad55942a9bbc5b20e1e623cf7fb1868f695d1" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddca131f3e7f2ce2df364b57949a9d47915cfbd35e46cfee355ccebbf794d6a2" -dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" -dependencies = [ - "cxx", - "cxx-build", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "inherent" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c05a410d53e44fc943a35a32ca27e32af2ea004d5107ccef685d022fc2b9fb" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "is-terminal" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24fddda5af7e54bf7da53067d6e802dbcc381d0a8eef629df528e3ebf68755cb" -dependencies = [ - "hermit-abi", - "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "itertools" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a9df60778f789c37f76778ae8d0a2471c41baa8b059d98a5873c978f549587" - -[[package]] -name = "itoa" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" - -[[package]] -name = "js-sys" -version = "0.3.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" -dependencies = [ - "spin 0.4.10", -] - -[[package]] -name = "libc" -version = "0.2.152" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" - -[[package]] -name = "libm" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" - -[[package]] -name = "libsqlite3-sys" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "link-cplusplus" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dfb9f65d9966f6ca6522043978030b564f3291af987fbf1dd55b6a064ba1b36" -dependencies = [ - "cc", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" - -[[package]] -name = "lock_api" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "matches" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e" - -[[package]] -name = "md-5" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6a38fc55c8bbc10058782919516f88826e70320db6d206aebc49611d24216ae" -dependencies = [ - "digest", -] - -[[package]] -name = "memchr" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" - -[[package]] -name = "minimal-lexical" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6595bb28ed34f43c3fe088e48f6cfb2e033cab45f25a5384d5fdf564fbc8c4b2" - -[[package]] -name = "miniz_oxide" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be0f75932c1f6cfae3c04000e40114adf955636e19040f9c0a2c380702aa1c7f" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.45.0", -] - -[[package]] -name = "nom" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1" -dependencies = [ - "memchr", - "minimal-lexical", - "version_check", -] - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-bigint-dig" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2399c9463abc5f909349d8aa9ba080e0b88b3ce2885389b60b993f39b1a56905" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand", - "smallvec", - "zeroize", -] - -[[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.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" -dependencies = [ - "libc", -] - -[[package]] -name = "object" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "optd-persistent" -version = "0.1.0" -dependencies = [ - "async-stream", - "async-trait", - "sea-orm", - "sea-orm-migration", - "serde_json", - "strum", - "tokio", - "trait-variant", -] - -[[package]] -name = "ordered-float" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d84eb1409416d254e4a9c8fa56cc24701755025b458f0fcd8e59e1f5f40c23bf" -dependencies = [ - "num-traits", -] - -[[package]] -name = "ouroboros" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c86de06555b970aec45229b27291b53154f21a5743a163419f4e4c0b065dcde" -dependencies = [ - "aliasable", - "ouroboros_macro", - "static_assertions", -] - -[[package]] -name = "ouroboros_macro" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3633d65683f13b9bcfaa3150880b018899fb0e5d0542f4adaea4f503fdb5eabf" -dependencies = [ - "heck 0.4.1", - "itertools 0.12.0", - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "parking" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" - -[[package]] -name = "parking_lot" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2f4f894f3865f6c0e02810fc597300f34dc2510f66400da262d8ae10e75767d" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys 0.29.0", -] - -[[package]] -name = "paste" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" - -[[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.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "pin-project-lite" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" - -[[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 = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" - -[[package]] -name = "proc-macro-error" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d259aa4825fa1a2371419d30a520219feff9fb3591550a209b4477d2ebaae4f" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.98", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd21889899aa8e1ca2b924c1d3f08086631fc90768225b3268b5d5c3e806a503" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", - "syn-mid", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "proc-macro2-diagnostics" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "606c4ba35817e2922a308af55ad51bab3645b59eae5c570d4a6cf07e36bd493b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", - "version_check", - "yansi", -] - -[[package]] -name = "quote" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" -dependencies = [ - "rand_core", -] - -[[package]] -name = "redox_syscall" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" -dependencies = [ - "bitflags 1.1.0", -] - -[[package]] -name = "redox_users" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" -dependencies = [ - "getrandom", - "redox_syscall", -] - -[[package]] -name = "regex" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72457500f2cf948feb4efccaeb460570c8f66ee5ba33c936bb4bfaa628d71853" -dependencies = [ - "byteorder", - "regex-syntax", - "utf8-ranges", -] - -[[package]] -name = "regex-syntax" -version = "0.6.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" - -[[package]] -name = "ring" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb9d44f9bf6b635117787f72416783eb7e4227aaf255e5ce739563d817176a7e" -dependencies = [ - "cc", - "getrandom", - "libc", - "spin 0.9.8", - "untrusted", - "windows-sys 0.48.0", -] - -[[package]] -name = "rsa" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dd2017d3e6d67384f301f8b06fbf4567afc576430a61624d845eb04d2b30a72" -dependencies = [ - "byteorder", - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-iter", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core", - "signature", - "subtle", - "zeroize", -] - -[[package]] -name = "rust_decimal" -version = "1.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee9164faf726e4f3ece4978b25ca877ddc6802fa77f38cdccb32c7f805ecd70c" -dependencies = [ - "arrayvec", - "num-traits", - "serde", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95" - -[[package]] -name = "rustix" -version = "0.38.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" -dependencies = [ - "bitflags 2.4.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.23.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4828ea528154ae444e5a642dbb7d5623354030dc9822b83fd9bb79683c7399d0" -dependencies = [ - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pemfile" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e4980fa29e4c4b212ffb3db068a564cbf560e51d3944b7c88bd8bf5bec64f4" -dependencies = [ - "base64 0.21.0", - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" - -[[package]] -name = "rustls-webpki" -version = "0.102.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "ryu" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "scratch" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e114536316b51a5aa7a0e59fc49661fd263c5507dd08bd28de052e57626ce69" - -[[package]] -name = "sea-bae" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd3534a9978d0aa7edd2808dc1f8f31c4d0ecd31ddf71d997b3c98e9f3c9114" -dependencies = [ - "heck 0.4.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "sea-orm" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5680a8b686985116607ef5f5af2b1f9e1cc2c228330e93101816a0baa279afa" -dependencies = [ - "async-stream", - "async-trait", - "bigdecimal", - "chrono", - "futures", - "log", - "ouroboros", - "rust_decimal", - "sea-orm-macros", - "sea-query", - "sea-query-binder", - "serde", - "serde_json", - "sqlx", - "strum", - "thiserror", - "time", - "tracing", - "url", - "uuid", -] - -[[package]] -name = "sea-orm-cli" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aefbd960c9ed7b2dfbab97b11890f5d8c314ad6e2f68c7b36c73ea0967fcc25" -dependencies = [ - "chrono", - "clap", - "dotenvy", - "glob", - "regex", - "sea-schema", - "tracing", - "tracing-subscriber", - "url", -] - -[[package]] -name = "sea-orm-macros" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a239e3bb1b566ad4ec2654d0d193d6ceddfd733487edc9c21a64d214c773910" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "sea-bae", - "syn 2.0.52", - "unicode-ident", -] - -[[package]] -name = "sea-orm-migration" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa7bbfbe3bec60b5925193acc9c98b9f8ae9853f52c8004df0c1ea5193c01ea0" -dependencies = [ - "async-trait", - "clap", - "dotenvy", - "futures", - "sea-orm", - "sea-orm-cli", - "sea-schema", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "sea-query" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff504d13b5e4b52fffcf2fb203d0352a5722fa5151696db768933e41e1e591bb" -dependencies = [ - "bigdecimal", - "chrono", - "inherent", - "ordered-float", - "rust_decimal", - "sea-query-derive", - "serde_json", - "time", - "uuid", -] - -[[package]] -name = "sea-query-binder" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0019f47430f7995af63deda77e238c17323359af241233ec768aba1faea7608" -dependencies = [ - "bigdecimal", - "chrono", - "rust_decimal", - "sea-query", - "serde_json", - "sqlx", - "time", - "uuid", -] - -[[package]] -name = "sea-query-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9834af2c4bd8c5162f00c89f1701fb6886119a88062cf76fe842ea9e232b9839" -dependencies = [ - "darling", - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 2.0.52", - "thiserror", -] - -[[package]] -name = "sea-schema" -version = "0.16.0-rc.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a4ff9e87c4340affbec4f7790d724dcd87e71fcd0ffe2247481843380485aa" -dependencies = [ - "futures", - "sea-query", - "sea-schema-derive", -] - -[[package]] -name = "sea-schema-derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "debdc8729c37fdbf88472f97fd470393089f997a909e535ff67c544d18cfccf0" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "serde" -version = "1.0.194" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.194" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "serde_json" -version = "1.0.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" -dependencies = [ - "itoa 1.0.1", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" -dependencies = [ - "form_urlencoded", - "itoa 0.4.0", - "ryu", - "serde", -] - -[[package]] -name = "sha1" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77f4e7f65455545c2153c1253d25056825e77ee2533f0e41deb65a93a34852f" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signature" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" -dependencies = [ - "digest", - "rand_core", -] - -[[package]] -name = "slab" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" -dependencies = [ - "serde", -] - -[[package]] -name = "socket2" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" -dependencies = [ - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "spin" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceac490aa12c567115b40b7b7fceca03a6c9d53d5defea066123debc83c5dc1f" - -[[package]] -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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a5be806ab6f127c3da44b7378837ebf01dadca8510a0e572460216b228bd0e" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "sqlformat" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a" -dependencies = [ - "itertools 0.10.0", - "nom", - "unicode_categories", -] - -[[package]] -name = "sqlx" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93334716a037193fac19df402f8571269c84a00852f6a7066b5d2616dcd64d3e" -dependencies = [ - "sqlx-core", - "sqlx-macros", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", -] - -[[package]] -name = "sqlx-core" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d8060b456358185f7d50c55d9b5066ad956956fddec42ee2e8567134a8936e" -dependencies = [ - "atoi", - "bigdecimal", - "byteorder", - "bytes", - "chrono", - "crc", - "crossbeam-queue", - "either", - "event-listener", - "futures-channel", - "futures-core", - "futures-intrusive", - "futures-io", - "futures-util", - "hashbrown", - "hashlink", - "hex", - "indexmap", - "log", - "memchr", - "once_cell", - "paste", - "percent-encoding", - "rust_decimal", - "rustls", - "rustls-pemfile", - "serde", - "serde_json", - "sha2", - "smallvec", - "sqlformat", - "thiserror", - "time", - "tokio", - "tokio-stream", - "tracing", - "url", - "uuid", - "webpki-roots", -] - -[[package]] -name = "sqlx-macros" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657" -dependencies = [ - "proc-macro2", - "quote", - "sqlx-core", - "sqlx-macros-core", - "syn 2.0.52", -] - -[[package]] -name = "sqlx-macros-core" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1804e8a7c7865599c9c79be146dc8a9fd8cc86935fa641d3ea58e5f0688abaa5" -dependencies = [ - "dotenvy", - "either", - "heck 0.5.0", - "hex", - "once_cell", - "proc-macro2", - "quote", - "serde", - "serde_json", - "sha2", - "sqlx-core", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", - "syn 2.0.52", - "tempfile", - "tokio", - "url", -] - -[[package]] -name = "sqlx-mysql" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" -dependencies = [ - "atoi", - "base64 0.22.0", - "bigdecimal", - "bitflags 2.4.0", - "byteorder", - "bytes", - "chrono", - "crc", - "digest", - "dotenvy", - "either", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "generic-array", - "hex", - "hkdf", - "hmac", - "itoa 1.0.1", - "log", - "md-5", - "memchr", - "once_cell", - "percent-encoding", - "rand", - "rsa", - "rust_decimal", - "serde", - "sha1", - "sha2", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror", - "time", - "tracing", - "uuid", - "whoami", -] - -[[package]] -name = "sqlx-postgres" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" -dependencies = [ - "atoi", - "base64 0.22.0", - "bigdecimal", - "bitflags 2.4.0", - "byteorder", - "chrono", - "crc", - "dotenvy", - "etcetera", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "hex", - "hkdf", - "hmac", - "home", - "itoa 1.0.1", - "log", - "md-5", - "memchr", - "num-bigint", - "once_cell", - "rand", - "rust_decimal", - "serde", - "serde_json", - "sha2", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror", - "time", - "tracing", - "uuid", - "whoami", -] - -[[package]] -name = "sqlx-sqlite" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680" -dependencies = [ - "atoi", - "chrono", - "flume", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "libsqlite3-sys", - "log", - "percent-encoding", - "serde", - "serde_urlencoded", - "sqlx-core", - "time", - "tracing", - "url", - "uuid", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "stringprep" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strum" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f" - -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - -[[package]] -name = "syn" -version = "1.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn-mid" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "tempfile" -version = "3.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" -dependencies = [ - "cfg-if", - "fastrand", - "rustix", - "windows-sys 0.52.0", -] - -[[package]] -name = "termcolor" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a52c023823933499250b43960b272e25336c6e2ab8684672edc34489f049ccdd" -dependencies = [ - "wincolor", -] - -[[package]] -name = "thiserror" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "thread_local" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" -dependencies = [ - "once_cell", -] - -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa 1.0.1", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tokio" -version = "1.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3ce25f50619af8b0aec2eb23deebe84249e19e2ddd393a6e16e3300a6dadfd" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "pin-project-lite", - "socket2", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "tokio-stream" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" -dependencies = [ - "matchers", - "once_cell", - "regex", - "sharded-slab", - "thread_local", - "tracing", - "tracing-core", -] - -[[package]] -name = "trait-variant" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "typenum" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" - -[[package]] -name = "unicode-bidi" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2560b941fdb9ea38301b9b708504d612fcdf9c91a8c31d82219bd74cb07d304d" -dependencies = [ - "matches", -] - -[[package]] -name = "unicode-ident" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" - -[[package]] -name = "unicode-normalization" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f" - -[[package]] -name = "unicode-width" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc85732b6d55a0d520aaf765536a188d9d993770c28633422f85bb646da61335" - -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" -dependencies = [ - "form_urlencoded", - "idna", - "matches", - "percent-encoding", -] - -[[package]] -name = "utf8-ranges" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" - -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "uuid" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" -dependencies = [ - "serde", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn 1.0.98", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.98", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" - -[[package]] -name = "web-sys" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721c6263e2c66fd44501cc5efbfa2b7dfa775d13e4ea38c46299646ed1f9c70a" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-roots" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de2cfda980f21be5a7ed2eadb3e6fe074d56022bea2cdeb1a62eb220fc04188" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "whoami" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524b58fa5a20a2fb3014dd6358b70e6579692a56ef6fce928834e488f42f65e8" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "winapi" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3ad91d846a4a5342c1fb7008d26124ee6cf94a3953751618577295373b32117" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a16a8e2ebfc883e2b1771c6482b1fb3c6831eab289ba391619a2d93a7356220f" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca29cb03c8ceaf20f8224a18a530938305e9872b1478ea24ff44b4f503a1d1d" - -[[package]] -name = "wincolor" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9dc3aa9dcda98b5a16150c54619c1ead22e3d3a5d458778ae914be760aa981a" -dependencies = [ - "winapi", -] - -[[package]] -name = "windows" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceb069ac8b2117d36924190469735767f0990833935ab430155e71a44bafe148" -dependencies = [ - "windows_aarch64_msvc 0.29.0", - "windows_i686_gnu 0.29.0", - "windows_i686_msvc 0.29.0", - "windows_x86_64_gnu 0.29.0", - "windows_x86_64_msvc 0.29.0", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d027175d00b01e0cbeb97d6ab6ebe03b12330a35786cbaca5252b1c4bf5d9b" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8793f59f7b8e8b01eda1a652b2697d87b93097198ae85f823b969ca5b89bba58" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" - -[[package]] -name = "windows_i686_msvc" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8602f6c418b67024be2996c512f5f995de3ba417f4c75af68401ab8756796ae4" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d615f419543e0bd7d2b3323af0d86ff19cbc4f816e6453f36a2c2ce889c354" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d95421d9ed3672c280884da53201a5c46b7b2765ca6faf34b0d71cf34a3561" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" - -[[package]] -name = "yansi" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" - -[[package]] -name = "zerocopy" -version = "0.7.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.52", -] - -[[package]] -name = "zeroize" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/optd-persistent/Cargo.toml b/optd-persistent/Cargo.toml deleted file mode 100644 index 50af728..0000000 --- a/optd-persistent/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "optd-persistent" -version = "0.1.0" -edition = "2021" -authors = ["Sarvesh Tandon", "Connor Tsui"] - -[dependencies] -sea-orm = { version = "1.1.1", features = [ - "sqlx-sqlite", - "runtime-tokio-rustls", - "macros", - "debug-print", - "with-json", -] } -sea-orm-migration = "1.0.0" -serde_json = "1.0.118" # Hash implementation on serde_json::Value -tokio = { version = "1.0.1", features = ["macros", "rt-multi-thread"] } -trait-variant = "0.1.2" - -# Pin more recent versions for -Zminimal-versions -async-trait = "0.1.43" -async-stream = "0.3.1" -strum = "0.26.1" -num_enum = "0.7.3" diff --git a/optd-persistent/README.md b/optd-persistent/README.md deleted file mode 100644 index 4a44127..0000000 --- a/optd-persistent/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Generate the `entities` module - -To make changes to the database tables and schema, you will have to modify files in the `migrator` module and then update the `entities` module using `sea-orm-cli`. - -This assumes that you already have the `sqlite3` binary installed. First, make sure you have installed `sea-orm-cli`: - -```sh -$ cargo install sea-orm-cli -``` - -Make sure your working directory is in the crate root: - -```sh -$ cd optd-persistent -``` - -If you have not generate the `sqlite.db` file yet, you will need to run this command which will generate the `sqlite.db` file and run all of the migrations: - -```sh -$ cargo run --bin migrate -``` - -Finally, run this command to generate / overwrite the `entities` module in the `src` directory. - -``` -$ sea-orm-cli generate entity -u sqlite:./sqlite.db -o src/entities -``` - -# Demo - -To run the demo, run the root binary crate: - -```sh -$ cargo run --bin optd-persistent -``` diff --git a/optd-persistent/src/bin/init.rs b/optd-persistent/src/bin/init.rs deleted file mode 100644 index a0b6b25..0000000 --- a/optd-persistent/src/bin/init.rs +++ /dev/null @@ -1,404 +0,0 @@ -use optd_persistent::cost_model::interface::AttrType; -use optd_persistent::cost_model::interface::ConstraintType; -use optd_persistent::cost_model::interface::IndexType; -use optd_persistent::cost_model::interface::StatType; -use optd_persistent::entities::*; -use optd_persistent::migrate; -use optd_persistent::TEST_DATABASE_FILENAME; -use optd_persistent::TEST_DATABASE_URL; -use sea_orm::sqlx::types::chrono::Utc; -use sea_orm::*; -use serde_json::json; - -async fn init_all_tables() -> Result<(), sea_orm::error::DbErr> { - let _ = std::fs::remove_file(TEST_DATABASE_FILENAME); - - let db = Database::connect(TEST_DATABASE_URL.clone()) - .await - .expect("Unable to connect to the database"); - - migrate(&db) - .await - .expect("Something went wrong during migration"); - - // Inserting into database_metadata - let database_metadata = database_metadata::ActiveModel { - id: Set(1), - name: Set("database1".to_owned()), - creation_time: Set(Utc::now()), - }; - database_metadata::Entity::insert(database_metadata) - .exec(&db) - .await - .expect("Unable to insert database metadata"); - - // Inserting into namespace_metadata - let namespace_metadata = namespace_metadata::ActiveModel { - id: Set(1), - database_id: Set(1), - name: Set("default".to_owned()), - creation_time: Set(Utc::now()), - }; - namespace_metadata::Entity::insert(namespace_metadata) - .exec(&db) - .await - .expect("Unable to insert namespace metadata"); - - // Inserting into table_metadata - let table_metadata = table_metadata::ActiveModel { - id: Set(1), - namespace_id: Set(1), - name: Set("users".to_owned()), - creation_time: Set(Utc::now()), - }; - table_metadata::Entity::insert(table_metadata) - .exec(&db) - .await - .expect("Unable to insert table metadata"); - - // Inserting into attribute - let attribute1 = attribute::ActiveModel { - id: Set(1), - table_id: Set(1), - name: Set("user_id".to_owned()), - compression_method: Set("N".to_owned()), - variant_tag: Set(AttrType::Integer as i32), - base_attribute_number: Set(0), - is_not_null: Set(true), - }; - let attribute2 = attribute::ActiveModel { - id: Set(2), - table_id: Set(1), - name: Set("username".to_owned()), - compression_method: Set("N".to_owned()), - variant_tag: Set(AttrType::Varchar as i32), - base_attribute_number: Set(1), - is_not_null: Set(true), - }; - attribute::Entity::insert(attribute1) - .exec(&db) - .await - .expect("Unable to insert attribute"); - attribute::Entity::insert(attribute2) - .exec(&db) - .await - .expect("Unable to insert attribute"); - - // Inserting into event - let event = event::ActiveModel { - epoch_id: Set(1), - source_variant: Set("execution_engine".to_owned()), - timestamp: Set(Utc::now()), - data: Set(json!({"dba": "parpulse"})), - }; - event::Entity::insert(event) - .exec(&db) - .await - .expect("Unable to insert event"); - - // Inserting into statistic - - // Table statistic - let table_statistic = statistic::ActiveModel { - id: Set(1), - name: Set("row_count".to_owned()), - table_id: Set(Some(1)), - creation_time: Set(Utc::now()), - number_of_attributes: Set(0), - variant_tag: Set(StatType::TableRowCount as i32), - description: Set("".to_owned()), - }; - let table_versioned_statistic = versioned_statistic::ActiveModel { - id: Set(1), - epoch_id: Set(1), - statistic_id: Set(1), - statistic_value: Set(json!(0)), - }; - statistic::Entity::insert(table_statistic) - .exec(&db) - .await - .expect("Unable to insert statistic"); - versioned_statistic::Entity::insert(table_versioned_statistic) - .exec(&db) - .await - .expect("Unable to insert versioned statistic"); - - // Single-column attribute statistic - let single_column_attribute_statistic = statistic::ActiveModel { - id: Set(2), - name: Set("cardinality".to_owned()), - table_id: Set(Some(1)), - creation_time: Set(Utc::now()), - number_of_attributes: Set(1), - variant_tag: Set(StatType::Cardinality as i32), - description: Set("1".to_owned()), - }; - let single_column_attribute_versioned_statistic = versioned_statistic::ActiveModel { - id: Set(2), - epoch_id: Set(1), - statistic_id: Set(2), - statistic_value: Set(json!(0)), - }; - statistic::Entity::insert(single_column_attribute_statistic) - .exec(&db) - .await - .expect("Unable to insert statistic"); - versioned_statistic::Entity::insert(single_column_attribute_versioned_statistic) - .exec(&db) - .await - .expect("Unable to insert versioned statistic"); - - let single_column_statistic_to_attribute_junction = - statistic_to_attribute_junction::ActiveModel { - statistic_id: Set(2), // cardinality - attribute_id: Set(1), // user_id - }; - statistic_to_attribute_junction::Entity::insert(single_column_statistic_to_attribute_junction) - .exec(&db) - .await - .expect("Unable to insert statistic_to_attribute_junction"); - - // Multi-column attribute statistic - let multi_column_attribute_statistic = statistic::ActiveModel { - id: Set(3), - name: Set("joint_cardinality".to_owned()), - table_id: Set(Some(1)), - creation_time: Set(Utc::now()), - number_of_attributes: Set(2), - variant_tag: Set(StatType::Cardinality as i32), - description: Set("1,2".to_owned()), - }; - let multi_column_attribute_versioned_statistic = versioned_statistic::ActiveModel { - id: Set(3), - epoch_id: Set(1), - statistic_id: Set(3), - statistic_value: Set(json!(0)), - }; - statistic::Entity::insert(multi_column_attribute_statistic) - .exec(&db) - .await - .expect("Unable to insert statistic"); - versioned_statistic::Entity::insert(multi_column_attribute_versioned_statistic) - .exec(&db) - .await - .expect("Unable to insert versioned statistic"); - - let multi_column_statistic_to_attribute_junction1 = - statistic_to_attribute_junction::ActiveModel { - statistic_id: Set(3), // joint cardinality - attribute_id: Set(1), // user_id - }; - let multi_column_statistic_to_attribute_junction2 = - statistic_to_attribute_junction::ActiveModel { - statistic_id: Set(3), // joint cardinality - attribute_id: Set(2), // username - }; - statistic_to_attribute_junction::Entity::insert(multi_column_statistic_to_attribute_junction1) - .exec(&db) - .await - .expect("Unable to insert statistic_to_attribute_junction"); - statistic_to_attribute_junction::Entity::insert(multi_column_statistic_to_attribute_junction2) - .exec(&db) - .await - .expect("Unable to insert statistic_to_attribute_junction"); - - // Inserting into index_metadata - let index_metadata = index_metadata::ActiveModel { - id: Set(1), - name: Set("user_id_index".to_owned()), - table_id: Set(1), - is_unique: Set(true), - nulls_not_distinct: Set(false), - is_primary: Set(true), - is_clustered: Set(false), - is_exclusion: Set(false), - variant_tag: Set(IndexType::Hash as i32), - number_of_attributes: Set(1), - description: Set("1".to_owned()), - }; - index_metadata::Entity::insert(index_metadata) - .exec(&db) - .await - .expect("Unable to insert index metadata"); - - // Inserting into trigger - let trigger = trigger::ActiveModel { - id: Set(1), - name: Set("after_insert_user".to_owned()), - table_id: Set(1), - parent_trigger_id: Set(1), - function: Set(json!(r#"{"function": "insert"}"#)), - }; - trigger::Entity::insert(trigger) - .exec(&db) - .await - .expect("Unable to insert trigger"); - - // Inserting into constraint_metadata - let constraint_metadata = constraint_metadata::ActiveModel { - id: Set(1), - name: Set("pk_user_id".to_owned()), - variant_tag: Set(ConstraintType::PrimaryKey as i32), - table_id: Set(Some(1)), - index_id: Set(Some(1)), - foreign_ref_id: Set(None), - check_src: Set("hello".to_owned()), - }; - constraint_metadata::Entity::insert(constraint_metadata) - .exec(&db) - .await - .expect("Unable to insert constraint metadata"); - - // Inserting into attribute_constraint_junction - let attribute_constraint_junction = attribute_constraint_junction::ActiveModel { - attribute_id: Set(1), - constraint_id: Set(1), - }; - attribute_constraint_junction::Entity::insert(attribute_constraint_junction) - .exec(&db) - .await - .expect("Unable to insert attribute_constraint_junction"); - - // Inserting into attribute_foreign_constraint_junction - let attribute_foreign_constraint_junction = - attribute_foreign_constraint_junction::ActiveModel { - attribute_id: Set(1), - constraint_id: Set(1), - }; - attribute_foreign_constraint_junction::Entity::insert(attribute_foreign_constraint_junction) - .exec(&db) - .await - .expect("Unable to insert attribute_foreign_constraint_junction"); - - // Inserting into cascades_group - let cascades_group = cascades_group::ActiveModel { - id: Set(1), - latest_winner: Set(None), - in_progress: Set(true), - is_optimized: Set(false), - }; - cascades_group::Entity::insert(cascades_group) - .exec(&db) - .await - .expect("Unable to insert cascades group"); - - // Inserting into logical_expression - let logical_expression = logical_expression::ActiveModel { - id: Set(1), - group_id: Set(1), - fingerprint: Set(12345), - variant_tag: Set(0), - data: Set(json!(r#"{"expr": "index_scan"}"#)), - }; - logical_expression::Entity::insert(logical_expression) - .exec(&db) - .await - .expect("Unable to insert logical expression"); - - // Inserting into physical_expression - let physical_expression = physical_expression::ActiveModel { - id: Set(1), - group_id: Set(1), - fingerprint: Set(12345), - variant_tag: Set(0), - data: Set(json!(r#"{"expr": "index_scan"}"#)), - }; - physical_expression::Entity::insert(physical_expression) - .exec(&db) - .await - .expect("Unable to insert physical expression"); - - // Inserting into physical_property - let physical_property = physical_property::ActiveModel { - id: Set(1), - physical_expression_id: Set(1), - variant_tag: Set(0), - data: Set(json!(r#"{"property": "indexed"}"#)), - }; - physical_property::Entity::insert(physical_property) - .exec(&db) - .await - .expect("Unable to insert physical property"); - - // Inserting into logical_property - let logical_property = logical_property::ActiveModel { - id: Set(1), - group_id: Set(1), - variant_tag: Set(0), - data: Set(json!(r#"{"property": "indexed"}"#)), - }; - logical_property::Entity::insert(logical_property) - .exec(&db) - .await - .expect("Unable to insert logical property"); - - let logical_children = logical_children::ActiveModel { - logical_expression_id: Set(1), - group_id: Set(1), - }; - logical_children::Entity::insert(logical_children) - .exec(&db) - .await - .expect("Unable to insert logical children"); - - let physical_children = physical_children::ActiveModel { - physical_expression_id: Set(1), - group_id: Set(1), - }; - physical_children::Entity::insert(physical_children) - .exec(&db) - .await - .expect("Unable to insert physical children"); - - // Inserting into plan_cost - let plan_cost = plan_cost::ActiveModel { - id: Set(1), - physical_expression_id: Set(1), - epoch_id: Set(1), - cost: Set(Some(json!({"compute_cost":10, "io_cost":10}))), - estimated_statistic: Set(Some(10.0)), - is_valid: Set(true), - }; - plan_cost::Entity::insert(plan_cost) - .exec(&db) - .await - .expect("Unable to insert plan cost"); - - // Inserting into physical_expression_to_statistic_junction - let physical_expression_to_statistic_junction = - physical_expression_to_statistic_junction::ActiveModel { - physical_expression_id: Set(1), - statistic_id: Set(1), - }; - physical_expression_to_statistic_junction::Entity::insert( - physical_expression_to_statistic_junction, - ) - .exec(&db) - .await - .expect("Unable to insert physical_expression_to_statistic_junction"); - - // Inserting into group_winner - let group_winner = group_winner::ActiveModel { - id: Set(1), - group_id: Set(1), - physical_expression_id: Set(1), - cost_id: Set(1), - epoch_id: Set(1), - }; - group_winner::Entity::insert(group_winner) - .exec(&db) - .await - .expect("Unable to insert group winner"); - - Ok(()) -} - -#[tokio::main] -async fn main() { - if let Err(e) = init_all_tables().await { - eprintln!("Error initializing database: {}", e); - std::process::exit(1); - } - - println!("Database initialized successfully"); -} diff --git a/optd-persistent/src/bin/migrate.rs b/optd-persistent/src/bin/migrate.rs deleted file mode 100644 index d6a36a6..0000000 --- a/optd-persistent/src/bin/migrate.rs +++ /dev/null @@ -1,16 +0,0 @@ -use optd_persistent::{migrate, DATABASE_FILENAME, DATABASE_URL}; -use sea_orm::*; -use sea_orm_migration::prelude::*; - -#[tokio::main] -async fn main() { - let _ = std::fs::remove_file(DATABASE_FILENAME); - - let db = Database::connect(DATABASE_URL) - .await - .expect("Unable to connect to the database"); - - migrate(&db) - .await - .expect("Something went wrong during migration"); -} diff --git a/optd-persistent/src/cost_model/catalog/mock_catalog.rs b/optd-persistent/src/cost_model/catalog/mock_catalog.rs deleted file mode 100644 index 5b2e28e..0000000 --- a/optd-persistent/src/cost_model/catalog/mock_catalog.rs +++ /dev/null @@ -1,165 +0,0 @@ -use sea_orm::prelude::Json; -use serde_json::json; - -use crate::cost_model::interface::{AttrType, IndexType, StatType}; - -/// TODO: documentation -pub struct MockDatabaseMetadata { - pub id: i32, - pub name: String, -} - -pub struct MockNamespaceMetadata { - pub id: i32, - pub name: String, - pub database_id: i32, -} - -pub struct MockTableMetadata { - pub id: i32, - pub name: String, - pub namespace_id: i32, -} - -pub struct MockAttribute { - pub id: i32, - pub name: String, - pub attr_index: i32, - pub table_id: i32, - pub compression_method: char, - pub attr_type: i32, - pub is_not_null: bool, -} - -pub struct MockStatistic { - pub id: i32, - pub stat_type: i32, - pub stat_value: Json, - pub attr_ids: Vec, - pub table_id: Option, - pub name: String, -} - -pub struct MockIndex { - pub id: i32, - pub name: String, - pub table_id: i32, - pub number_of_attributes: i32, - pub index_type: i32, - pub is_unique: bool, - pub nulls_not_distinct: bool, - pub is_primary: bool, - pub is_clustered: bool, - pub is_exclusion: bool, - pub attr_ids: Vec, -} - -pub struct MockTrigger { - pub id: i32, - pub name: String, - pub table_id: i32, - pub parent_trigger_id: i32, - pub function: String, -} - -/// TODO: documentation -#[derive(Default)] -pub struct MockCatalog { - pub databases: Vec, - pub namespaces: Vec, - pub tables: Vec, - pub attributes: Vec, - pub statistics: Vec, - pub indexes: Vec, - pub triggers: Vec, - // TODO: constraints -} - -impl MockCatalog { - /// TODO: documentation - pub fn new() -> Self { - let databases: Vec = vec![MockDatabaseMetadata { - id: 1, - name: "db1".to_string(), - }]; - let namespaces: Vec = vec![MockNamespaceMetadata { - id: 1, - name: "ns1".to_string(), - database_id: 1, - }]; - let tables: Vec = vec![MockTableMetadata { - id: 1, - name: "table1".to_string(), - namespace_id: 1, - }]; - let attributes: Vec = vec![ - MockAttribute { - id: 1, - name: "attr1".to_string(), - attr_index: 1, - table_id: 1, - compression_method: 'n', - attr_type: AttrType::Integer as i32, - is_not_null: true, - }, - MockAttribute { - id: 2, - name: "attr2".to_string(), - attr_index: 2, - table_id: 1, - compression_method: 'n', - attr_type: AttrType::Integer as i32, - is_not_null: false, - }, - ]; - let statistics: Vec = vec![ - MockStatistic { - id: 1, - stat_type: StatType::NonNullCount as i32, - stat_value: json!(100), - attr_ids: vec![1], - table_id: None, - name: "CountAttr1".to_string(), - }, - MockStatistic { - id: 2, - stat_type: StatType::NonNullCount as i32, - stat_value: json!(200), - attr_ids: vec![2], - table_id: None, - name: "CountAttr2".to_string(), - }, - MockStatistic { - id: 3, - stat_type: StatType::TableRowCount as i32, - stat_value: json!(300), - attr_ids: vec![], - table_id: Some(1), - name: "Table1Count".to_string(), - }, - ]; - let indexes: Vec = vec![MockIndex { - id: 1, - name: "index1".to_string(), - table_id: 1, - number_of_attributes: 1, - index_type: IndexType::Hash as i32, - is_unique: false, - nulls_not_distinct: false, - is_primary: true, - is_clustered: false, - is_exclusion: false, - attr_ids: vec![1], - }]; - - MockCatalog { - databases, - namespaces, - tables, - attributes, - statistics, - indexes, - triggers: vec![], - } - } -} diff --git a/optd-persistent/src/cost_model/catalog/mod.rs b/optd-persistent/src/cost_model/catalog/mod.rs deleted file mode 100644 index 4df69d8..0000000 --- a/optd-persistent/src/cost_model/catalog/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod mock_catalog; diff --git a/optd-persistent/src/cost_model/interface.rs b/optd-persistent/src/cost_model/interface.rs deleted file mode 100644 index e4e4ceb..0000000 --- a/optd-persistent/src/cost_model/interface.rs +++ /dev/null @@ -1,177 +0,0 @@ -#![allow(dead_code, unused_imports)] - -use crate::entities::cascades_group; -use crate::entities::logical_expression; -use crate::entities::physical_expression; -use crate::StorageResult; -use num_enum::{IntoPrimitive, TryFromPrimitive}; -use sea_orm::prelude::Json; -use sea_orm::*; -use sea_orm_migration::prelude::*; -use serde_json::json; -use std::sync::Arc; - -pub type GroupId = i32; -pub type TableId = i32; -pub type AttrId = i32; -pub type ExprId = i32; -pub type EpochId = i32; -pub type StatId = i32; -pub type AttrIndex = i32; - -/// TODO: documentation -pub enum CatalogSource { - Iceberg(), - Mock, -} - -/// TODO: documentation -#[repr(i32)] -#[derive(Copy, Clone, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)] -pub enum AttrType { - Integer = 1, - Float, - Varchar, - Boolean, -} - -/// TODO: documentation -pub enum IndexType { - BTree, - Hash, -} - -/// TODO: documentation -pub enum ConstraintType { - PrimaryKey, - ForeignKey, - Unique, - Check, -} - -/// TODO: documentation -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum StatType { - /// The row count in a table. `TableRowCount` only applies to table statistics. - TableRowCount, - /// The number of non-null values in a column. - NonNullCount, - /// The number of distinct values in a column. - Cardinality, - /// The minimum value in a column. - Min, - /// The maximum value in a column. - Max, - /// The frequency of each value in a column. - MostCommonValues, - /// The distribution of values in a column. - Distribution, -} - -/// TODO: documentation -#[derive(PartialEq)] -pub enum EpochOption { - // TODO(lanlou): Could I make i32 -> EpochId? - Existed(i32), - New(String, String), -} - -/// TODO: documentation -#[derive(Clone, Debug)] -pub struct Stat { - pub stat_type: StatType, - pub stat_value: Json, - pub attr_ids: Vec, - pub table_id: Option, - pub name: String, -} - -/// TODO: documentation -#[derive(Clone, Debug, PartialEq)] -pub struct Cost { - pub compute_cost: f64, - pub io_cost: f64, -} - -#[derive(Clone, Debug)] -pub struct Attr { - pub table_id: i32, - pub name: String, - pub compression_method: String, - pub attr_type: AttrType, - pub base_index: i32, - pub nullable: bool, -} - -/// TODO: documentation -#[trait_variant::make(Send)] -pub trait CostModelStorageLayer { - async fn create_new_epoch(&self, source: String, data: String) -> StorageResult; - - async fn update_stats_from_catalog(&self, c: CatalogSource) -> StorageResult; - - async fn update_stats( - &self, - stat: Stat, - epoch_option: EpochOption, - ) -> StorageResult>; - - async fn store_cost( - &self, - expr_id: ExprId, - cost: Option, - estimated_statistic: Option, - epoch_id: Option, - ) -> StorageResult<()>; - - async fn store_expr_stats_mappings( - &self, - expr_id: ExprId, - stat_ids: Vec, - ) -> StorageResult<()>; - - /// Get the statistics for a given table. - /// - /// If `epoch_id` is None, it will return the latest statistics. - async fn get_stats_for_table( - &self, - table_id: TableId, - stat_type: StatType, - epoch_id: Option, - ) -> StorageResult>; - - /// Get the (joint) statistics for one or more attributes. - /// - /// If `epoch_id` is None, it will return the latest statistics. - async fn get_stats_for_attr( - &self, - attr_ids: Vec, - stat_type: StatType, - epoch_id: Option, - ) -> StorageResult>; - - /// Get the (joint) statistics for one or more attributes based on attribute base indices. - /// - /// If `epoch_id` is None, it will return the latest statistics. - async fn get_stats_for_attr_indices_based( - &self, - table_id: TableId, - attr_base_indices: Vec, - stat_type: StatType, - epoch_id: Option, - ) -> StorageResult>; - - async fn get_cost_analysis( - &self, - expr_id: ExprId, - epoch_id: EpochId, - ) -> StorageResult<(Option, Option)>; - - async fn get_cost(&self, expr_id: ExprId) -> StorageResult<(Option, Option)>; - - async fn get_attribute( - &self, - table_id: TableId, - attribute_base_index: AttrIndex, - ) -> StorageResult>; -} diff --git a/optd-persistent/src/cost_model/mod.rs b/optd-persistent/src/cost_model/mod.rs deleted file mode 100644 index 46514a1..0000000 --- a/optd-persistent/src/cost_model/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod catalog; - -pub mod interface; -pub mod orm; diff --git a/optd-persistent/src/cost_model/orm.rs b/optd-persistent/src/cost_model/orm.rs deleted file mode 100644 index 74c03f1..0000000 --- a/optd-persistent/src/cost_model/orm.rs +++ /dev/null @@ -1,1478 +0,0 @@ -#![allow(dead_code, unused_imports, unused_variables)] - -use crate::cost_model::interface::Cost; -use crate::entities::{prelude::*, *}; -use crate::{BackendError, BackendManager, CostModelStorageLayer, StorageResult}; -use sea_orm::prelude::{Expr, Json}; -use sea_orm::sea_query::{ExprTrait, Query}; -use sea_orm::{sqlx::types::chrono::Utc, EntityTrait}; -use sea_orm::{ - ActiveModelTrait, ColumnTrait, Condition, DbBackend, DbErr, DeleteResult, EntityOrSelect, - ModelTrait, QueryFilter, QueryOrder, QuerySelect, QueryTrait, RuntimeErr, TransactionTrait, -}; -use serde_json::json; - -use super::catalog::mock_catalog::{self, MockCatalog}; -use super::interface::{ - Attr, AttrId, AttrIndex, AttrType, CatalogSource, EpochId, EpochOption, ExprId, Stat, StatId, - StatType, TableId, -}; - -impl BackendManager { - /// The description is to concat `attr_ids` using commas - /// Note that `attr_ids` should be sorted before concatenation - /// e.g. [1, 2, 3] -> "1,2,3" - fn get_description_from_attr_ids(&self, mut attr_ids: Vec) -> String { - attr_ids.sort(); - attr_ids - .iter() - .map(|id| id.to_string()) - .collect::>() - .join(",") - } -} - -impl CostModelStorageLayer for BackendManager { - /// TODO: documentation - async fn create_new_epoch(&self, source: String, data: String) -> StorageResult { - let new_event = event::ActiveModel { - source_variant: sea_orm::ActiveValue::Set(source), - timestamp: sea_orm::ActiveValue::Set(Utc::now()), - data: sea_orm::ActiveValue::Set(sea_orm::JsonValue::String(data)), - ..Default::default() - }; - let insert_res = Event::insert(new_event).exec(&self.db).await?; - Ok(insert_res.last_insert_id) - } - - /// TODO: documentation - async fn update_stats_from_catalog(&self, c: CatalogSource) -> StorageResult { - let transaction = self.db.begin().await?; - let source = match c { - CatalogSource::Mock => "Mock", - CatalogSource::Iceberg() => "Iceberg", - }; - let new_event = event::ActiveModel { - source_variant: sea_orm::ActiveValue::Set(source.to_string()), - timestamp: sea_orm::ActiveValue::Set(Utc::now()), - data: sea_orm::ActiveValue::Set(sea_orm::JsonValue::String( - "Update stats from catalog".to_string(), - )), - ..Default::default() - }; - let epoch_id = Event::insert(new_event) - .exec(&transaction) - .await? - .last_insert_id; - match c { - CatalogSource::Mock => { - let mock_catalog = MockCatalog::new(); - DatabaseMetadata::insert_many(mock_catalog.databases.iter().map(|database| { - database_metadata::ActiveModel { - name: sea_orm::ActiveValue::Set(database.name.clone()), - creation_time: sea_orm::ActiveValue::Set(Utc::now()), - ..Default::default() - } - })) - .exec(&transaction) - .await?; - NamespaceMetadata::insert_many(mock_catalog.namespaces.iter().map(|namespace| { - namespace_metadata::ActiveModel { - name: sea_orm::ActiveValue::Set(namespace.name.clone()), - database_id: sea_orm::ActiveValue::Set(namespace.database_id), - creation_time: sea_orm::ActiveValue::Set(Utc::now()), - ..Default::default() - } - })) - .exec(&transaction) - .await?; - TableMetadata::insert_many(mock_catalog.tables.iter().map(|table| { - table_metadata::ActiveModel { - name: sea_orm::ActiveValue::Set(table.name.clone()), - namespace_id: sea_orm::ActiveValue::Set(table.namespace_id), - creation_time: sea_orm::ActiveValue::Set(Utc::now()), - ..Default::default() - } - })) - .exec(&transaction) - .await?; - Attribute::insert_many(mock_catalog.attributes.iter().map(|attr| { - attribute::ActiveModel { - table_id: sea_orm::ActiveValue::Set(attr.table_id), - name: sea_orm::ActiveValue::Set(attr.name.clone()), - compression_method: sea_orm::ActiveValue::Set( - attr.compression_method.to_string(), - ), - variant_tag: sea_orm::ActiveValue::Set(attr.attr_type), - base_attribute_number: sea_orm::ActiveValue::Set(attr.attr_index), - is_not_null: sea_orm::ActiveValue::Set(attr.is_not_null), - ..Default::default() - } - })) - .exec(&transaction) - .await?; - Statistic::insert_many(mock_catalog.statistics.iter().map(|stat| { - statistic::ActiveModel { - name: sea_orm::ActiveValue::Set(stat.name.clone()), - table_id: sea_orm::ActiveValue::Set(stat.table_id), - number_of_attributes: sea_orm::ActiveValue::Set(stat.attr_ids.len() as i32), - creation_time: sea_orm::ActiveValue::Set(Utc::now()), - variant_tag: sea_orm::ActiveValue::Set(stat.stat_type), - description: sea_orm::ActiveValue::Set( - self.get_description_from_attr_ids(stat.attr_ids.clone()), - ), - ..Default::default() - } - })) - .exec(&transaction) - .await?; - VersionedStatistic::insert_many(mock_catalog.statistics.iter().map(|stat| { - versioned_statistic::ActiveModel { - epoch_id: sea_orm::ActiveValue::Set(epoch_id), - statistic_id: sea_orm::ActiveValue::Set(stat.id), - statistic_value: sea_orm::ActiveValue::Set(stat.stat_value.clone()), - ..Default::default() - } - })) - .exec(&transaction) - .await?; - StatisticToAttributeJunction::insert_many(mock_catalog.statistics.iter().flat_map( - |stat| { - stat.attr_ids.iter().map(move |attr_id| { - statistic_to_attribute_junction::ActiveModel { - statistic_id: sea_orm::ActiveValue::Set(stat.id), - attribute_id: sea_orm::ActiveValue::Set(*attr_id), - } - }) - }, - )) - .exec(&transaction) - .await?; - IndexMetadata::insert_many( - mock_catalog - .indexes - .iter() - .map(|index| index_metadata::ActiveModel { - name: sea_orm::ActiveValue::Set(index.name.clone()), - table_id: sea_orm::ActiveValue::Set(index.table_id), - number_of_attributes: sea_orm::ActiveValue::Set( - index.attr_ids.len() as i32 - ), - variant_tag: sea_orm::ActiveValue::Set(index.index_type), - is_unique: sea_orm::ActiveValue::Set(index.is_unique), - nulls_not_distinct: sea_orm::ActiveValue::Set(index.nulls_not_distinct), - is_primary: sea_orm::ActiveValue::Set(index.is_primary), - is_clustered: sea_orm::ActiveValue::Set(index.is_clustered), - is_exclusion: sea_orm::ActiveValue::Set(index.is_exclusion), - description: sea_orm::ActiveValue::Set( - self.get_description_from_attr_ids(index.attr_ids.clone()), - ), - ..Default::default() - }), - ) - .exec(&transaction) - .await?; - // TODO: initialize constraints - } - CatalogSource::Iceberg() => todo!(), - } - transaction.commit().await?; - Ok(epoch_id) - } - - /// TODO: improve the documentation - /* Update the statistics in the database. - * The statistic can be newly inserted or updated. If the statistic value - * is the same as the latest existing one, the update will be ignored, and - * the return value will be None. - * If `epoch_option` is `EpochOption::Existed(epoch_id)`, the new statistic - * will be associated with the given epoch_id. If `epoch_option` is - * `EpochOption::New(source, data)`, a new epoch will be created with the - * given source and data, and the new statistic will be associated with the - * new epoch. And return the new epoch_id. - * If the statistic value is the same as the latest existing one, this function - * won't create a new epoch. - * - * For batch updates, the caller can directly call this function with - * New epoch option at the first time, and if the epoch_id is returned, the - * caller can use the returned epoch_id for the rest of the updates. - * But if the epoch_id is not returned, the caller should continue using - * the New epoch option for the next statistic update. - */ - async fn update_stats( - &self, - stat: Stat, - epoch_option: EpochOption, - ) -> StorageResult> { - let transaction = self.db.begin().await?; - // 0. Check if the stat already exists. If exists, get stat_id, else insert into statistic table. - let stat_id = match stat.table_id { - Some(table_id) => { - // TODO: only select needed fields - let res = Statistic::find() - .filter(statistic::Column::TableId.eq(table_id)) - .inner_join(versioned_statistic::Entity) - .select_also(versioned_statistic::Entity) - .order_by_desc(versioned_statistic::Column::EpochId) - .one(&transaction) - .await?; - match res { - Some(stat_data) => { - if stat_data.1.unwrap().statistic_value == stat.stat_value { - return Ok(None); - } - stat_data.0.id - } - None => { - let new_stat = statistic::ActiveModel { - name: sea_orm::ActiveValue::Set(stat.name.clone()), - table_id: sea_orm::ActiveValue::Set(Some(table_id)), - number_of_attributes: sea_orm::ActiveValue::Set( - stat.attr_ids.len() as i32 - ), - creation_time: sea_orm::ActiveValue::Set(Utc::now()), - variant_tag: sea_orm::ActiveValue::Set(stat.stat_type as i32), - description: sea_orm::ActiveValue::Set("".to_string()), - ..Default::default() - }; - let res = Statistic::insert(new_stat).exec(&transaction).await; - match res { - Ok(insert_res) => insert_res.last_insert_id, - Err(_) => { - return Err(BackendError::CostModel( - format!( - "failed to insert statistic {:?} into statistic table", - stat - ) - .into(), - )) - } - } - } - } - } - None => { - let description = self.get_description_from_attr_ids(stat.attr_ids.clone()); - let res = Statistic::find() - .filter(statistic::Column::NumberOfAttributes.eq(stat.attr_ids.len() as i32)) - .filter(statistic::Column::Description.eq(description.clone())) - .filter(statistic::Column::VariantTag.eq(stat.stat_type as i32)) - .inner_join(versioned_statistic::Entity) - .select_also(versioned_statistic::Entity) - .order_by_desc(versioned_statistic::Column::EpochId) - .one(&transaction) - .await?; - match res { - Some(stat_data) => { - if stat_data.1.unwrap().statistic_value == stat.stat_value { - return Ok(None); - } - stat_data.0.id - } - None => { - let new_stat = statistic::ActiveModel { - name: sea_orm::ActiveValue::Set(stat.name.clone()), - number_of_attributes: sea_orm::ActiveValue::Set( - stat.attr_ids.len() as i32 - ), - creation_time: sea_orm::ActiveValue::Set(Utc::now()), - variant_tag: sea_orm::ActiveValue::Set(stat.stat_type as i32), - description: sea_orm::ActiveValue::Set(description), - ..Default::default() - }; - // TODO(lanlou): we should not clone here maybe... - let insert_res = Statistic::insert(new_stat.clone()) - .exec(&transaction) - .await?; - for attr_id in stat.attr_ids { - let new_junction = statistic_to_attribute_junction::ActiveModel { - statistic_id: sea_orm::ActiveValue::Set(insert_res.last_insert_id), - attribute_id: sea_orm::ActiveValue::Set(attr_id), - }; - let res = StatisticToAttributeJunction::insert(new_junction) - .exec(&transaction) - .await?; - } - insert_res.last_insert_id - } - } - } - }; - // 1. Insert into attr_stats and related junction tables. - let epoch_id = match epoch_option { - EpochOption::Existed(e) => e, - EpochOption::New(source, data) => { - let new_event = event::ActiveModel { - source_variant: sea_orm::ActiveValue::Set(source), - timestamp: sea_orm::ActiveValue::Set(Utc::now()), - data: sea_orm::ActiveValue::Set(sea_orm::JsonValue::String(data)), - ..Default::default() - }; - let insert_res = Event::insert(new_event).exec(&transaction).await?; - insert_res.last_insert_id - } - }; - let new_stats = versioned_statistic::ActiveModel { - epoch_id: sea_orm::ActiveValue::Set(epoch_id), - statistic_id: sea_orm::ActiveValue::Set(stat_id), - statistic_value: sea_orm::ActiveValue::Set(stat.stat_value), - ..Default::default() - }; - let _ = VersionedStatistic::insert(new_stats) - .exec(&transaction) - .await?; - - // 2. Invalidate all the related cost. - let _ = plan_cost::Entity::update_many() - .col_expr(plan_cost::Column::IsValid, Expr::value(false)) - .filter(plan_cost::Column::IsValid.eq(true)) - .filter(plan_cost::Column::EpochId.lt(epoch_id)) - .filter( - plan_cost::Column::PhysicalExpressionId.in_subquery( - Query::select() - .column( - physical_expression_to_statistic_junction::Column::PhysicalExpressionId, - ) - .from(physical_expression_to_statistic_junction::Entity) - .cond_where( - physical_expression_to_statistic_junction::Column::StatisticId - .eq(stat_id), - ) - .to_owned(), - ), - ) - .exec(&transaction) - .await?; - - transaction.commit().await?; - Ok(Some(epoch_id)) - } - - /// TODO: documentation - async fn store_expr_stats_mappings( - &self, - expr_id: ExprId, - stat_ids: Vec, - ) -> StorageResult<()> { - let to_insert_mappings = stat_ids - .iter() - .map( - |stat_id| physical_expression_to_statistic_junction::ActiveModel { - physical_expression_id: sea_orm::ActiveValue::Set(expr_id), - statistic_id: sea_orm::ActiveValue::Set(*stat_id), - }, - ) - .collect::>(); - let _ = PhysicalExpressionToStatisticJunction::insert_many(to_insert_mappings) - .exec(&self.db) - .await?; - Ok(()) - } - - /// TODO: documentation - async fn get_stats_for_table( - &self, - table_id: TableId, - stat_type: StatType, - epoch_id: Option, - ) -> StorageResult> { - match epoch_id { - Some(epoch_id) => Ok(VersionedStatistic::find() - .filter(versioned_statistic::Column::EpochId.eq(epoch_id)) - .inner_join(statistic::Entity) - .filter(statistic::Column::TableId.eq(table_id)) - .filter(statistic::Column::VariantTag.eq(stat_type as i32)) - .one(&self.db) - .await? - .map(|stat| stat.statistic_value)), - - None => Ok(VersionedStatistic::find() - .inner_join(statistic::Entity) - .filter(statistic::Column::TableId.eq(table_id)) - .filter(statistic::Column::VariantTag.eq(stat_type as i32)) - .order_by_desc(versioned_statistic::Column::EpochId) - .one(&self.db) - .await? - .map(|stat| stat.statistic_value)), - } - } - - /// TODO: documentation - async fn get_stats_for_attr( - &self, - mut attr_ids: Vec, - stat_type: StatType, - epoch_id: Option, - ) -> StorageResult> { - let attr_num = attr_ids.len() as i32; - attr_ids.sort(); - let description = self.get_description_from_attr_ids(attr_ids); - - // We don't join with junction table here for faster lookup. - match epoch_id { - Some(epoch_id) => Ok(VersionedStatistic::find() - .filter(versioned_statistic::Column::EpochId.eq(epoch_id)) - .inner_join(statistic::Entity) - .filter(statistic::Column::NumberOfAttributes.eq(attr_num)) - .filter(statistic::Column::Description.eq(description)) - .filter(statistic::Column::VariantTag.eq(stat_type as i32)) - .one(&self.db) - .await? - .map(|stat| stat.statistic_value)), - - None => Ok(VersionedStatistic::find() - .inner_join(statistic::Entity) - .filter(statistic::Column::NumberOfAttributes.eq(attr_num)) - .filter(statistic::Column::Description.eq(description)) - .filter(statistic::Column::VariantTag.eq(stat_type as i32)) - .order_by_desc(versioned_statistic::Column::EpochId) - .one(&self.db) - .await? - .map(|stat| stat.statistic_value)), - } - } - - async fn get_stats_for_attr_indices_based( - &self, - table_id: TableId, - attr_base_indices: Vec, - stat_type: StatType, - epoch_id: Option, - ) -> StorageResult> { - // Get the attribute ids based on table id and attribute base indices - let mut condition = Condition::any(); - for attr_base_index in &attr_base_indices { - condition = condition.add(attribute::Column::BaseAttributeNumber.eq(*attr_base_index)); - } - let attr_ids = Attribute::find() - .filter(attribute::Column::TableId.eq(table_id)) - .filter(condition) - .all(&self.db) - .await? - .iter() - .map(|attr| attr.id) - .collect::>(); - - if attr_ids.len() != attr_base_indices.len() { - return Err(BackendError::CostModel( - format!( - "Not all attributes found for table_id {} and base indices {:?}", - table_id, attr_base_indices - ) - .into(), - )); - } - - self.get_stats_for_attr(attr_ids, stat_type, epoch_id).await - } - - /// TODO: documentation - /// Each record in the `plan_cost` table can contain either the cost or the estimated statistic - /// or both, but never neither. - /// The name can be misleading, since it can also return the estimated statistic. - async fn get_cost_analysis( - &self, - expr_id: ExprId, - epoch_id: EpochId, - ) -> StorageResult<(Option, Option)> { - let cost = PlanCost::find() - .filter(plan_cost::Column::PhysicalExpressionId.eq(expr_id)) - .filter(plan_cost::Column::EpochId.eq(epoch_id)) - .one(&self.db) - .await?; - // When this cost is not found, we should return None - if cost.is_none() { - return Ok((None, None)); - } - - let real_cost = cost.as_ref().and_then(|c| c.cost.as_ref()).map(|c| Cost { - compute_cost: c.get("compute_cost").unwrap().as_f64().unwrap(), - io_cost: c.get("io_cost").unwrap().as_f64().unwrap(), - }); - - Ok((real_cost, cost.unwrap().estimated_statistic)) - } - - /// TODO: documentation - /// It returns the cost and estimated statistic if applicable. - /// Each record in the `plan_cost` table can contain either the cost or the estimated statistic - /// or both, but never neither. - /// The name can be misleading, since it can also return the estimated statistic. - async fn get_cost(&self, expr_id: ExprId) -> StorageResult<(Option, Option)> { - let cost = PlanCost::find() - .filter(plan_cost::Column::PhysicalExpressionId.eq(expr_id)) - .order_by_desc(plan_cost::Column::EpochId) - .one(&self.db) - .await?; - // When this cost is invalid or not found, we should return None - if cost.is_none() || !cost.clone().unwrap().is_valid { - return Ok((None, None)); - } - - let real_cost = cost.as_ref().and_then(|c| c.cost.as_ref()).map(|c| Cost { - compute_cost: c.get("compute_cost").unwrap().as_f64().unwrap(), - io_cost: c.get("io_cost").unwrap().as_f64().unwrap(), - }); - - Ok((real_cost, cost.unwrap().estimated_statistic)) - } - - /// This method should handle the case when the cost is already stored. - /// The name maybe misleading, since it can also store the estimated statistic. - /// If epoch_id is none, we pick the latest epoch_id. - /// - /// TODO: consider whether we need to pass the epoch_id here. When the epoch is - /// stale because someone else updates the stats while we're still computing cost, - /// what is the expected behavior? - /// - /// TODO: documentation - async fn store_cost( - &self, - physical_expression_id: ExprId, - cost: Option, - estimated_statistic: Option, - epoch_id: Option, - ) -> StorageResult<()> { - assert!(cost.is_some() || estimated_statistic.is_some()); - // TODO: we shouldn't do the following checks in the production environment. - let expr_exists = PhysicalExpression::find_by_id(physical_expression_id) - .one(&self.db) - .await?; - if expr_exists.is_none() { - return Err(BackendError::CostModel( - format!( - "physical expression id {} not found when storing cost", - physical_expression_id - ) - .into(), - )); - } - // Check if epoch_id exists in Event table - if epoch_id.is_some() { - let epoch_exists = Event::find() - .filter(event::Column::EpochId.eq(epoch_id.unwrap())) - .one(&self.db) - .await - .unwrap(); - if epoch_exists.is_none() { - return Err(BackendError::CostModel( - format!("epoch id {} not found when storing cost", epoch_id.unwrap()).into(), - )); - } - } - - let transaction = self.db.begin().await?; - - /* - The `store_cost` logic is as follows: - 1. If the epoch_id is provided, we should update the cost with the corresponding epoch_id, - or insert a new record if it doesn't exist. - 2. If the epoch_id is not provided, we cannot directly use the latest epoch_id, since in the - plan_cost table, for the current physical expression, there may be a valid cost with a lower - epoch_id, since the update_stats function updates unrelated stats. So we need to handle the - epoch_id in following logics: - 1) If a valid cost is already in the plan_cost table, we use the same epoch_id. - 2) If there is no valid cost in the plan_cost table, or there is no record, we use the - latest epoch_id. - */ - // TODO: We should add some integration tests to fully test the above logic - let epoch_id_data; - let existed_cost; - if let Some(epoch_id) = epoch_id { - epoch_id_data = epoch_id; - existed_cost = PlanCost::find() - .filter(plan_cost::Column::PhysicalExpressionId.eq(physical_expression_id)) - .filter(plan_cost::Column::EpochId.eq(epoch_id)) - .one(&transaction) - .await?; - } else { - existed_cost = PlanCost::find() - .filter(plan_cost::Column::PhysicalExpressionId.eq(physical_expression_id)) - .filter(plan_cost::Column::IsValid.eq(true)) - .order_by_desc(plan_cost::Column::EpochId) - .one(&transaction) - .await?; - if existed_cost.is_none() { - epoch_id_data = { - // When init, please make sure there is at least one epoch in the Event table. - let latest_epoch_id = Event::find() - .order_by_desc(event::Column::EpochId) - .one(&self.db) - .await? - .unwrap(); - latest_epoch_id.epoch_id - } - } else { - epoch_id_data = existed_cost.clone().unwrap().epoch_id; - } - } - - if existed_cost.is_some() { - let mut new_cost: plan_cost::ActiveModel = existed_cost.unwrap().into(); - let mut update = false; - if cost.is_some() { - let input_cost = sea_orm::ActiveValue::Set(Some(json!({ - "compute_cost": cost.clone().unwrap().compute_cost, - "io_cost": cost.clone().unwrap().io_cost - }))); - if new_cost.cost != input_cost { - update = true; - new_cost.cost = input_cost; - } - } - if estimated_statistic.is_some() { - let input_estimated_statistic = sea_orm::ActiveValue::Set(estimated_statistic); - if new_cost.estimated_statistic != input_estimated_statistic { - update = true; - new_cost.estimated_statistic = input_estimated_statistic; - } - } - if update { - assert!(new_cost.epoch_id.is_unchanged()); - let _ = PlanCost::update(new_cost).exec(&transaction).await?; - } - } else { - // TODO: we shouldn't do the following checks in the production environment. - // This check may be easy to violate, so consider removing epoch_id input parameter. - let latest_cost = PlanCost::find() - .filter(plan_cost::Column::PhysicalExpressionId.eq(physical_expression_id)) - .order_by_desc(plan_cost::Column::EpochId) - .one(&transaction) - .await?; - if latest_cost.is_some() { - assert!(latest_cost.clone().unwrap().epoch_id < epoch_id_data); - assert!(!latest_cost.clone().unwrap().is_valid); - } - - let new_cost = plan_cost::ActiveModel { - physical_expression_id: sea_orm::ActiveValue::Set(physical_expression_id), - epoch_id: sea_orm::ActiveValue::Set(epoch_id_data), - cost: sea_orm::ActiveValue::Set( - cost.map(|c| json!({"compute_cost": c.compute_cost, "io_cost": c.io_cost})), - ), - estimated_statistic: sea_orm::ActiveValue::Set(estimated_statistic), - is_valid: sea_orm::ActiveValue::Set(true), - ..Default::default() - }; - let _ = PlanCost::insert(new_cost).exec(&transaction).await?; - } - - transaction.commit().await?; - Ok(()) - } - - async fn get_attribute( - &self, - table_id: TableId, - attribute_base_index: AttrIndex, - ) -> StorageResult> { - let attr_res = Attribute::find() - .filter(attribute::Column::TableId.eq(table_id)) - .filter(attribute::Column::BaseAttributeNumber.eq(attribute_base_index)) - .one(&self.db) - .await?; - match attr_res { - Some(attr) => match AttrType::try_from(attr.variant_tag) { - Ok(attr_type) => Ok(Some(Attr { - table_id: attr.table_id, - name: attr.name, - compression_method: attr.compression_method, - attr_type, - base_index: attr.base_attribute_number, - nullable: attr.is_not_null, - })), - Err(_) => Err(BackendError::BackendError(format!( - "Failed to convert variant tag {} to AttrType", - attr.variant_tag - ))), - }, - None => Ok(None), - } - } -} - -// TODO: add integration tests -#[cfg(test)] -mod tests { - use crate::cost_model::interface::{Cost, EpochOption, StatType}; - use crate::{cost_model::interface::Stat, migrate, CostModelStorageLayer}; - use crate::{get_sqlite_url, TEST_DATABASE_FILE}; - use sea_orm::sqlx::database; - use sea_orm::sqlx::types::chrono::Utc; - use sea_orm::Statement; - use sea_orm::{ - ColumnTrait, ConnectionTrait, Database, DbBackend, EntityTrait, ModelTrait, QueryFilter, - QuerySelect, QueryTrait, - }; - use sea_orm_migration::schema::json; - use serde_json::{de, json}; - - use crate::entities::{prelude::*, *}; - - async fn run_migration(db_file: &str) -> String { - let database_url = get_sqlite_url(http://github.com/db_file); - remove_db_file(db_file); - - let db = Database::connect(database_url.clone()) - .await - .expect("Unable to connect to the database"); - - migrate(&db) - .await - .expect("Something went wrong during migration"); - - db.execute(sea_orm::Statement::from_string( - sea_orm::DatabaseBackend::Sqlite, - "PRAGMA foreign_keys = ON;".to_owned(), - )) - .await - .expect("Unable to enable foreign keys"); - database_url.clone() - } - - fn remove_db_file(db_file: &str) { - let database_file = format!("./{}", db_file); - let _ = std::fs::remove_file(database_file); - } - - async fn copy_init_db(db_file: &str) -> String { - let _ = std::fs::copy(TEST_DATABASE_FILE.clone(), db_file); - get_sqlite_url(http://github.com/db_file) - } - - #[tokio::test] - async fn test_create_new_epoch() { - const DATABASE_FILE: &str = "test_create_new_epoch.db"; - let database_url = run_migration(DATABASE_FILE).await; - let mut binding = super::BackendManager::new(Some(&database_url)).await; - let backend_manager = binding.as_mut().unwrap(); - let res = backend_manager - .create_new_epoch("source".to_string(), "data".to_string()) - .await; - println!("{:?}", res); - assert!(res.is_ok()); - let inserted_id = res.unwrap(); - let lookup_res = Event::find_by_id(inserted_id) - .all(&backend_manager.db) - .await - .unwrap(); - println!("{:?}", lookup_res); - assert_eq!(lookup_res.len(), 1); - assert_eq!(lookup_res[0].source_variant, "source"); - assert_eq!( - lookup_res[0].data, - serde_json::Value::String("data".to_string()) - ); - assert_eq!(lookup_res[0].epoch_id, inserted_id); - - remove_db_file(DATABASE_FILE); - } - - #[tokio::test] - async fn test_update_stats_from_catalog() { - const DATABASE_FILE: &str = "test_update_stats_from_catalog.db"; - let database_url = run_migration(DATABASE_FILE).await; - let mut binding = super::BackendManager::new(Some(&database_url)).await; - let backend_manager = binding.as_mut().unwrap(); - let res = backend_manager - .update_stats_from_catalog(super::CatalogSource::Mock) - .await; - println!("{:?}", res); - assert!(res.is_ok()); - let epoch_id = res.unwrap(); - assert_eq!(epoch_id, 1); - - let lookup_res = Statistic::find().all(&backend_manager.db).await.unwrap(); - println!("{:?}", lookup_res); - assert_eq!(lookup_res.len(), 3); - - let stat_res = backend_manager - .get_stats_for_table(1, StatType::TableRowCount, Some(epoch_id)) - .await; - assert!(stat_res.is_ok()); - assert_eq!(stat_res.unwrap().unwrap(), json!(300)); - let stat_res = backend_manager - .get_stats_for_attr([2].to_vec(), StatType::NonNullCount, None) - .await; - assert!(stat_res.is_ok()); - assert_eq!(stat_res.unwrap().unwrap(), json!(200)); - - remove_db_file(DATABASE_FILE); - } - - #[tokio::test] - async fn test_update_attr_stats() { - const DATABASE_FILE: &str = "test_update_attr_stats.db"; - let database_url = copy_init_db(DATABASE_FILE).await; - let mut binding = super::BackendManager::new(Some(&database_url)).await; - let backend_manager = binding.as_mut().unwrap(); - // 1. Update non-existed stat - let epoch_id1 = backend_manager - .create_new_epoch("test".to_string(), "test_update_attr_stats".to_string()) - .await - .unwrap(); - let stat = Stat { - stat_type: StatType::NonNullCount, - stat_value: json!(100), - attr_ids: vec![1], - table_id: None, - name: "countattr1".to_string(), - }; - let res = backend_manager - .update_stats(stat, EpochOption::Existed(epoch_id1)) - .await; - assert!(res.is_ok()); - let stat_res = Statistic::find() - .filter(statistic::Column::Name.eq("countattr1")) - .all(&backend_manager.db) - .await - .unwrap(); - assert_eq!(stat_res.len(), 1); - println!("{:?}", stat_res); - assert_eq!(stat_res[0].number_of_attributes, 1); - assert_eq!(stat_res[0].description, "1".to_string()); - assert_eq!(stat_res[0].variant_tag, StatType::NonNullCount as i32); - let stat_attr_res = StatisticToAttributeJunction::find() - .filter(statistic_to_attribute_junction::Column::StatisticId.eq(stat_res[0].id)) - .all(&backend_manager.db) - .await - .unwrap(); - assert_eq!(stat_attr_res.len(), 1); - assert_eq!(stat_attr_res[0].attribute_id, 1); - let versioned_stat_res = VersionedStatistic::find() - .filter(versioned_statistic::Column::StatisticId.eq(stat_res[0].id)) - .all(&backend_manager.db) - .await - .unwrap(); - assert_eq!(versioned_stat_res.len(), 1); - assert_eq!(versioned_stat_res[0].statistic_value, json!(100)); - assert_eq!(versioned_stat_res[0].epoch_id, epoch_id1); - - // 2. Normal update - // 2.1 Insert some costs - let res = PhysicalExpression::insert(physical_expression::ActiveModel { - group_id: sea_orm::ActiveValue::Set(1), - fingerprint: sea_orm::ActiveValue::Set(12346), - variant_tag: sea_orm::ActiveValue::Set(1), - data: sea_orm::ActiveValue::Set(serde_json::Value::String("data".to_string())), - ..Default::default() - }); - let expr_id = res.exec(&backend_manager.db).await.unwrap().last_insert_id; - let res = PhysicalExpressionToStatisticJunction::insert( - physical_expression_to_statistic_junction::ActiveModel { - physical_expression_id: sea_orm::ActiveValue::Set(expr_id), - statistic_id: sea_orm::ActiveValue::Set(stat_res[0].id), - }, - ) - .exec(&backend_manager.db) - .await - .unwrap(); - backend_manager - .store_cost( - expr_id, - Some(Cost { - compute_cost: 42.0, - io_cost: 42.0, - }), - Some(42.0), - Some(versioned_stat_res[0].epoch_id), - ) - .await - .unwrap(); - let cost_res = PlanCost::find() - .filter(plan_cost::Column::PhysicalExpressionId.eq(expr_id)) - .all(&backend_manager.db) - .await - .unwrap(); - assert_eq!(cost_res.len(), 1); - assert!(cost_res[0].is_valid); - // 2.2 Normal update - let epoch_id2 = backend_manager - .create_new_epoch("test".to_string(), "test_update_attr_stats".to_string()) - .await - .unwrap(); - let stat2 = Stat { - stat_type: StatType::NonNullCount, - stat_value: json!(200), - attr_ids: vec![1], - table_id: None, - name: "countattr1".to_string(), - }; - let res = backend_manager - .update_stats(stat2, EpochOption::Existed(epoch_id2)) - .await; - assert!(res.is_ok()); - // 2.3 Check statistic table - let stat_res = Statistic::find() - .filter(statistic::Column::Name.eq("countattr1")) - .all(&backend_manager.db) - .await - .unwrap(); - assert_eq!(stat_res.len(), 1); - assert_eq!(stat_res[0].description, "1".to_string()); - // 2.4 Check statistic_to_attribute_junction table - let stat_attr_res = StatisticToAttributeJunction::find() - .filter(statistic_to_attribute_junction::Column::StatisticId.eq(stat_res[0].id)) - .all(&backend_manager.db) - .await - .unwrap(); - assert_eq!(stat_attr_res.len(), 1); - assert_eq!(stat_attr_res[0].attribute_id, 1); - // 2.5 Check versioned_statistic table - let versioned_stat_res = VersionedStatistic::find() - .filter(versioned_statistic::Column::StatisticId.eq(stat_res[0].id)) - .all(&backend_manager.db) - .await - .unwrap(); - assert_eq!(versioned_stat_res.len(), 2); - assert_eq!(versioned_stat_res[0].statistic_value, json!(100)); - assert_eq!(versioned_stat_res[0].epoch_id, epoch_id1); - assert_eq!(versioned_stat_res[0].statistic_id, stat_res[0].id); - assert_eq!(versioned_stat_res[1].statistic_value, json!(200)); - assert_eq!(versioned_stat_res[1].epoch_id, epoch_id2); - assert_eq!(versioned_stat_res[1].statistic_id, stat_res[0].id); - assert!(epoch_id1 < epoch_id2); - // 2.6 Check plan_cost table (cost invalidation) - let cost_res = PlanCost::find() - .filter(plan_cost::Column::PhysicalExpressionId.eq(expr_id)) - .all(&backend_manager.db) - .await - .unwrap(); - assert_eq!(cost_res.len(), 1); - assert_eq!( - cost_res[0].cost, - Some(json!({"compute_cost": 42.0, "io_cost": 42.0})) - ); - assert_eq!(cost_res[0].epoch_id, epoch_id1); - assert!(!cost_res[0].is_valid); - - // 3. Update existed stat with the same value - let epoch_num = Event::find().all(&backend_manager.db).await.unwrap().len(); - let stat3 = Stat { - stat_type: StatType::NonNullCount, - stat_value: json!(200), - attr_ids: vec![1], - table_id: None, - name: "CountAttr1".to_string(), - }; - let res = backend_manager - .update_stats( - stat3, - EpochOption::New("source".to_string(), "data".to_string()), - ) - .await; - assert!(res.is_ok()); - assert!(res.unwrap().is_none()); - let epoch_num2 = Event::find().all(&backend_manager.db).await.unwrap().len(); - assert_eq!(epoch_num, epoch_num2); - - remove_db_file(DATABASE_FILE); - } - - #[tokio::test] - async fn test_update_table_stats() { - // Simulate batch updates, first insert an existed same stat with none epoch_id, - // then insert some non-existed or different stats with New epoch_option. - const DATABASE_FILE: &str = "test_update_table_stats.db"; - let database_url = copy_init_db(DATABASE_FILE).await; - let mut binding = super::BackendManager::new(Some(&database_url)).await; - let backend_manager = binding.as_mut().unwrap(); - - let table_inserted_res = TableMetadata::insert(table_metadata::ActiveModel { - name: sea_orm::ActiveValue::Set("Table2".to_string()), - namespace_id: sea_orm::ActiveValue::Set(1), - creation_time: sea_orm::ActiveValue::Set(Utc::now()), - ..Default::default() - }) - .exec(&backend_manager.db) - .await - .unwrap(); - - let statistics: Vec = vec![ - Stat { - stat_type: StatType::TableRowCount, - stat_value: json!(0), - attr_ids: vec![], - table_id: Some(1), - name: "row_count".to_string(), - }, - Stat { - stat_type: StatType::TableRowCount, - stat_value: json!(20), - attr_ids: vec![], - table_id: Some(1), - name: "row_count".to_string(), - }, - Stat { - stat_type: StatType::TableRowCount, - stat_value: json!(100), - attr_ids: vec![], - table_id: Some(table_inserted_res.last_insert_id), - name: "Table2Count1".to_string(), - }, - ]; - - let mut epoch_id: Option = None; - for stat in statistics { - match epoch_id { - Some(e) => { - let res = backend_manager - .update_stats(stat.clone(), EpochOption::Existed(e)) - .await; - assert!(res.is_ok()); - assert!(stat.name == "Table2Count1"); - let res = res.unwrap(); - assert!(res.is_some()); - assert!(res.unwrap() == e); - let stat_res = Statistic::find() - .filter(statistic::Column::Name.eq(stat.name.clone())) - .all(&backend_manager.db) - .await - .unwrap(); - assert_eq!(stat_res.len(), 1); - let versioned_stat_res = VersionedStatistic::find() - .filter(versioned_statistic::Column::StatisticId.eq(stat_res[0].id)) - .all(&backend_manager.db) - .await - .unwrap(); - assert_eq!(versioned_stat_res.len(), 1); - assert_eq!(versioned_stat_res[0].statistic_value, stat.stat_value); - assert_eq!(versioned_stat_res[0].epoch_id, e); - } - None => { - let res = backend_manager - .update_stats( - stat.clone(), - EpochOption::New("source".to_string(), "data".to_string()), - ) - .await; - assert!(res.is_ok()); - if stat.stat_value == json!(0) { - assert!(res.unwrap().is_none()); - } else { - assert!(stat.stat_value == json!(20)); - let res = res.unwrap(); - assert!(res.is_some()); - epoch_id = Some(res.unwrap()); - } - } - } - } - - remove_db_file(DATABASE_FILE); - } - - #[tokio::test] - async fn test_store_cost() { - const DATABASE_FILE: &str = "test_store_cost.db"; - let database_url = copy_init_db(DATABASE_FILE).await; - let mut binding = super::BackendManager::new(Some(&database_url)).await; - let backend_manager = binding.as_mut().unwrap(); - let epoch_id = backend_manager - .create_new_epoch("source".to_string(), "data".to_string()) - .await - .unwrap(); - let stat = Stat { - stat_type: StatType::TableRowCount, - stat_value: json!(10), - attr_ids: vec![], - table_id: Some(1), - name: "row_count".to_owned(), - }; - let res = backend_manager - .update_stats(stat, EpochOption::Existed(epoch_id)) - .await; - assert!(res.is_ok()); - - let physical_expression_id = 1; - let cost = Cost { - compute_cost: 42.0, - io_cost: 42.0, - }; - let mut estimated_statistic = 42.0; - backend_manager - .store_cost( - physical_expression_id, - Some(cost.clone()), - Some(estimated_statistic), - Some(epoch_id), - ) - .await - .unwrap(); - let costs = super::PlanCost::find() - .all(&backend_manager.db) - .await - .unwrap(); - assert_eq!(costs.len(), 2); // The first row one is the initialized data - assert_eq!(costs[1].epoch_id, epoch_id); - assert_eq!(costs[1].physical_expression_id, physical_expression_id); - assert_eq!( - costs[1].cost, - Some(json!({"compute_cost": cost.compute_cost, "io_cost": cost.io_cost})) - ); - assert_eq!(costs[1].estimated_statistic.unwrap(), estimated_statistic); - - estimated_statistic = 50.0; - backend_manager - .store_cost( - physical_expression_id, - None, - Some(estimated_statistic), - None, - ) - .await - .unwrap(); - let costs = super::PlanCost::find() - .all(&backend_manager.db) - .await - .unwrap(); - assert_eq!(costs.len(), 2); // We should not insert a new row - assert_eq!(costs[1].epoch_id, epoch_id); - assert_eq!(costs[1].physical_expression_id, physical_expression_id); - assert_eq!( - costs[1].cost, - Some(json!({"compute_cost": cost.compute_cost, "io_cost": cost.io_cost})) - ); - assert_eq!( - costs[1].estimated_statistic.unwrap(), - estimated_statistic // The estimated_statistic should be update - ); - - remove_db_file(DATABASE_FILE); - } - - #[tokio::test] - async fn test_get_cost() { - const DATABASE_FILE: &str = "test_get_cost.db"; - let database_url = copy_init_db(DATABASE_FILE).await; - let mut binding = super::BackendManager::new(Some(&database_url)).await; - let backend_manager = binding.as_mut().unwrap(); - let epoch_id = backend_manager - .create_new_epoch("source".to_string(), "data".to_string()) - .await - .unwrap(); - let stat = Stat { - stat_type: StatType::TableRowCount, - stat_value: json!(10), - attr_ids: vec![], - table_id: Some(1), - name: "row_count".to_owned(), - }; - let res = backend_manager - .update_stats(stat, EpochOption::Existed(epoch_id)) - .await; - assert!(res.is_ok()); - - let physical_expression_id = 1; - let cost = Cost { - compute_cost: 42.0, - io_cost: 42.0, - }; - let _ = backend_manager - .store_cost( - physical_expression_id, - Some(cost.clone()), - None, - Some(epoch_id), - ) - .await; - let costs = super::PlanCost::find() - .all(&backend_manager.db) - .await - .unwrap(); - assert_eq!(costs.len(), 2); // The first row one is the initialized data - assert_eq!(costs[1].epoch_id, epoch_id); - assert_eq!(costs[1].physical_expression_id, physical_expression_id); - assert_eq!( - costs[1].cost, - Some(json!({"compute_cost": cost.compute_cost, "io_cost": cost.io_cost})) - ); - assert_eq!(costs[1].estimated_statistic, None); - - let res = backend_manager - .get_cost(physical_expression_id) - .await - .unwrap(); - assert_eq!(res.0.unwrap(), cost); - assert_eq!(res.1, None); - - remove_db_file(DATABASE_FILE); - } - - #[tokio::test] - async fn test_get_cost_analysis() { - const DATABASE_FILE: &str = "test_get_cost_analysis.db"; - let database_url = copy_init_db(DATABASE_FILE).await; - let mut binding = super::BackendManager::new(Some(&database_url)).await; - let backend_manager = binding.as_mut().unwrap(); - let epoch_id = backend_manager - .create_new_epoch("source".to_string(), "data".to_string()) - .await - .unwrap(); - let stat = Stat { - stat_type: StatType::TableRowCount, - stat_value: json!(10), - attr_ids: vec![], - table_id: Some(1), - name: "row_count".to_owned(), - }; - let res = backend_manager - .update_stats(stat, EpochOption::Existed(epoch_id)) - .await; - assert!(res.is_ok()); - - let physical_expression_id = 1; - let estimated_statistic = 42.0; - let _ = backend_manager - .store_cost( - physical_expression_id, - None, - Some(estimated_statistic), - Some(epoch_id), - ) - .await; - let costs = super::PlanCost::find() - .all(&backend_manager.db) - .await - .unwrap(); - assert_eq!(costs.len(), 2); // The first row one is the initialized data - assert_eq!(costs[1].epoch_id, epoch_id); - assert_eq!(costs[1].physical_expression_id, physical_expression_id); - assert_eq!(costs[1].cost, None); - assert_eq!(costs[1].estimated_statistic.unwrap(), estimated_statistic); - println!("{:?}", costs); - - // Retrieve physical_expression_id 1 and epoch_id 1 - let res = backend_manager - .get_cost_analysis(physical_expression_id, 1) - .await - .unwrap(); - - // The cost in the dummy data is 10 - assert_eq!( - res.0.unwrap(), - Cost { - compute_cost: 10.0, - io_cost: 10.0, - } - ); - assert_eq!(res.1.unwrap(), 10.0); - - remove_db_file(DATABASE_FILE); - } - - #[tokio::test] - async fn test_get_stats_for_table() { - const DATABASE_FILE: &str = "test_get_stats_for_table.db"; - let database_url = copy_init_db(DATABASE_FILE).await; - let mut binding = super::BackendManager::new(Some(&database_url)).await; - let backend_manager = binding.as_mut().unwrap(); - let epoch_id = 1; - let table_id = 1; - let stat_type = StatType::TableRowCount; - - // Get initial stats - let res = backend_manager - .get_stats_for_table(table_id, stat_type, None) - .await - .unwrap() - .unwrap(); - let row_count = res.as_i64().unwrap(); - assert_eq!(row_count, 0); - - // Update stats - let epoch_id2 = backend_manager - .create_new_epoch("test".to_string(), "test_get_stats_for_table".to_string()) - .await - .unwrap(); - let stat = Stat { - stat_type: StatType::TableRowCount, - stat_value: json!(100), - attr_ids: vec![], - table_id: Some(table_id), - name: "row_count".to_string(), - }; - backend_manager - .update_stats(stat, EpochOption::Existed(epoch_id2)) - .await - .unwrap(); - - // Get updated stats - let res = backend_manager - .get_stats_for_table(table_id, stat_type, None) - .await - .unwrap() - .unwrap(); - let row_count = res.as_i64().unwrap(); - assert_eq!(row_count, 100); - - // Get stats for a specific epoch - let res = backend_manager - .get_stats_for_table(table_id, stat_type, Some(epoch_id)) - .await - .unwrap() - .unwrap(); - let row_count = res.as_i64().unwrap(); - assert_eq!(row_count, 0); - - remove_db_file(DATABASE_FILE); - } - - #[tokio::test] - async fn test_get_stats_for_single_attr() { - const DATABASE_FILE: &str = "test_get_stats_for_single_attr.db"; - let database_url = copy_init_db(DATABASE_FILE).await; - let mut binding = super::BackendManager::new(Some(&database_url)).await; - let backend_manager = binding.as_mut().unwrap(); - let epoch_id = 1; - let attr_ids = vec![1]; - let stat_type = StatType::Cardinality; - - // Get initial stats - let res = backend_manager - .get_stats_for_attr(attr_ids.clone(), stat_type, None) - .await - .unwrap() - .unwrap(); - let cardinality = res.as_i64().unwrap(); - assert_eq!(cardinality, 0); - - // Update stats - let epoch_id2 = backend_manager - .create_new_epoch( - "test".to_string(), - "test_get_stats_for_single_attr".to_string(), - ) - .await - .unwrap(); - let stat = Stat { - stat_type: StatType::Cardinality, - stat_value: json!(100), - attr_ids: attr_ids.clone(), - table_id: None, - name: "cardinality".to_string(), - }; - backend_manager - .update_stats(stat, EpochOption::Existed(epoch_id2)) - .await - .unwrap(); - - // Get updated stats - let res = backend_manager - .get_stats_for_attr(attr_ids.clone(), stat_type, None) - .await - .unwrap() - .unwrap(); - let cardinality = res.as_i64().unwrap(); - assert_eq!(cardinality, 100); - - // Get stats for a specific epoch - let res = backend_manager - .get_stats_for_attr(attr_ids.clone(), stat_type, Some(epoch_id)) - .await - .unwrap() - .unwrap(); - let cardinality = res.as_i64().unwrap(); - assert_eq!(cardinality, 0); - - remove_db_file(DATABASE_FILE); - } - - #[tokio::test] - async fn test_get_stats_for_multiple_attrs() { - const DATABASE_FILE: &str = "test_get_stats_for_multiple_attrs.db"; - let database_url = copy_init_db(DATABASE_FILE).await; - let mut binding = super::BackendManager::new(Some(&database_url)).await; - let backend_manager = binding.as_mut().unwrap(); - let epoch_id = 1; - let attr_ids = vec![2, 1]; - let stat_type = StatType::Cardinality; - - // Get initial stats - let res = backend_manager - .get_stats_for_attr(attr_ids.clone(), stat_type, None) - .await - .unwrap() - .unwrap(); - let cardinality = res.as_i64().unwrap(); - assert_eq!(cardinality, 0); - - // Update stats - let epoch_id2 = backend_manager - .create_new_epoch( - "test".to_string(), - "test_get_stats_for_multiple_attrs".to_string(), - ) - .await - .unwrap(); - let stat = Stat { - stat_type: StatType::Cardinality, - stat_value: json!(111), - attr_ids: attr_ids.clone(), - table_id: None, - name: "cardinality".to_string(), - }; - backend_manager - .update_stats(stat, EpochOption::Existed(epoch_id2)) - .await - .unwrap(); - - // Get updated stats - let res = backend_manager - .get_stats_for_attr(attr_ids.clone(), stat_type, None) - .await - .unwrap() - .unwrap(); - let cardinality = res.as_i64().unwrap(); - assert_eq!(cardinality, 111); - - // Get stats for a specific epoch - let res = backend_manager - .get_stats_for_attr(attr_ids.clone(), stat_type, Some(epoch_id)) - .await - .unwrap() - .unwrap(); - let cardinality = res.as_i64().unwrap(); - assert_eq!(cardinality, 0); - - remove_db_file(DATABASE_FILE); - } - - #[tokio::test] - async fn test_get_stats_for_attr_indices_based() { - const DATABASE_FILE: &str = "test_get_stats_for_attr_indices_based.db"; - let database_url = copy_init_db(DATABASE_FILE).await; - let mut binding = super::BackendManager::new(Some(&database_url)).await; - let backend_manager = binding.as_mut().unwrap(); - let epoch_id = 1; - let table_id = 1; - let attr_base_indices = vec![0, 1]; - let stat_type = StatType::Cardinality; - - // Statistics exist in the database - let res = backend_manager - .get_stats_for_attr_indices_based(table_id, attr_base_indices.clone(), stat_type, None) - .await - .unwrap() - .unwrap(); - let cardinality = res.as_i64().unwrap(); - assert_eq!(cardinality, 0); - - // Statistics do not exist in the database - let attr_base_indices = vec![1]; - let res = backend_manager - .get_stats_for_attr_indices_based(table_id, attr_base_indices.clone(), stat_type, None) - .await - .unwrap(); - assert!(res.is_none()); - - // Attribute base indices not valid. - let attr_base_indices = vec![1, 2]; - let res = backend_manager - .get_stats_for_attr_indices_based(table_id, attr_base_indices.clone(), stat_type, None) - .await; - assert!(res.is_err()); - - remove_db_file(DATABASE_FILE); - } -} diff --git a/optd-persistent/src/db/init.db b/optd-persistent/src/db/init.db deleted file mode 100644 index 0c3d71cbc9c86b448e4a0a1026ee9e6aec03b782..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 147456 zcmeI5TZ|jmdB>4P6qpeTx>K!COnc`%9uFyI%r4^5j3+!lEWoTf#ZhZI3l zz4SXna>&CO?#)uG75>D|l04^|bNPMWxqau5oO^xJ4lM1e>#dl9HYV&6L{a!XO%sIF zj|D+^fv)C5{3*RiwcgRQ*zvl@OG@MOU#<01Y@hOLCi9H)K>2mF2pPQHt zOEuWeuBgqH&z5J))2GXGtx~L5fw^b~CRLHuu&B*UYiG)nWnyr8eD3u4nKBExc;O7A z(hTX&AYSCIoEnHXlZ95ir{yzhu_(UxPKe8P7Ok5`tF-*PJg+o9a%;L(o~k zEYEPS8fHKWT&M?@A2%9OG8u(!GA)}+iSf2?)E)bJo$P19b*q-?bZA$1>eZ@mIBsAp z+J0b@B_xVbA~nxmF}+*8qAS(9A6TA6mYt9d-EZAwBB7ln2rOED#j|S-JB-`;Bep`V zxO#qPM!j@Nyk8I16)`ucd$#N)$EdiDA9yCUTH{)?!E8uqCvQ+2x+k==wq%di-BF@s ziOvpXQ0LMkyWwvyb~i*$oNt3s)Mi>?cluq~bKOZtT&tCCCM&II=hEu!OQH}qU;lcQ z##zIx2X3P|N9A8jxkhP2V!72ZIipTYi1(@UqamPuQ%5D@29ppe_cD|OSr|yv7`J91 zmv^Mqm5KEYB!PXWVI)NFpjktf%^X&DWi#r)fOy~K4I{FUY!8Kp%1vs~Yt9h1Hp*1o zm6~VytXpOS;<8KKclQ)yy#Z#y^r>~V1}UR4aP=CB*hs_1oTO3Wk$3WU&t}4Tz`co( zRs&hffcSSAUO9Z^)-=?6sHe%DUo$IKuNhj!qke^{D6rWykp1@qbEUTST#e-yCCm5V z?Wk7mH`l-3ok?fZQc1jbE(8-#E*_HRfyM z+@Dd89TV@K42i~4iv^^3p0W6cjB{hSwBm(ST0MGHyd2h|Ir91z%|4xq)%CWYt3g-f zATy95y0iFdUs|0T?$uTMmU+Eutk_Fl*g|}x)Ey$-*)L|);bHO4n~g$s-O(-I#}!NO zjt;rru)M}hwtJ@9*+r9)nYCKgwic;(r2fucqRFF1Bg5{e=-;kFp8l)3J%aKBx z%2$+^3jbdCUg28>uP{^4^FPo3dH&seCI375{kfm#zMK2w+;Z*}62}7sKmY_l00ck) z1V8`;K;VfWP*kPYQ!Aq*qsL1lqbH3K_9z*(K}`TSqlEltE_ zmpr##Gv2lx$MRSyU)&{~k4sfdzhcrxf)R={xx1v2M`D z8H;``pSmN)<0GB4OK$|OL0*}FZKG78Jpz`-%Q2C~vC@lrSU5}Ck@nIt9fJ7!Gom9%s#o>05S zYwWPF+fVdM_FA`h6u*<*;ZzciDA%w8Qc?v%CqcA3$mBlPmt?z1b%jfc=xlfh9$$c~Tcll3pZvIaTHwy=qZ;*Fj z=85R&PzewK0T2KI5C8!X009sHfk#W=Md@{Mecyt*wpeRfe_&9$xJGL2Rq_5K(o9^i z+n=y+O{rFP&yfp4*dp& zSIK7(kK3@%Ab(t1iQgWrtQk00JNY0w4eaAOHd&00JNY0w4ea+l9cem=Xr2TI>80JI6Cz zDh-d0%#W5%oftV)I{EpL<6~pTM~{w-oEjNn>;K(_4@BiZlz&pbr~I|@7mBBRLow*H z029h_hA z-#VYLJ4>SciGzGv8W5wC4$=-;IwUs_ELdtr8WR(aOGw#%NtMHM9r+a99-SEI+bvNB z{0V{meN^iBfq;D?RlVgGh(21-*#G~&pnU%cvkp`T1V8`;KmY_l00ck)1V8`;KmY`u zXaYlGzi>#NTC}d3^=i;#|No0$JlWp=7nOe&=noGN009sH0T2KI5C8!X009sH0T2Lz zZA9R6p0w4eaAOHd& z00JNY0w4eaAh10NbnO5CR8W4pJu87yg8&GC00@8p2!H?xfB*=900@8p2s8*B6mx?7 z5*_x>%Gs>Kz73GR`OmPOMeAm%asK~fLHQW8KmY_l00ck)1V8`;KmY_l00ck)1hxTz zF)=M1ljX8`HDD`zht~Ol!B+eFJ6H6pb*B>8u5(2{rCre*kyrHFdSm_nGeP;;HmD(r z2m&Ag0w4eaAOHd&00JNY0w4eaAg~qzm45i|a8{NDwf0u%6QC0S%T~4O=H(v=dGSL* zSyo;u{GgD}eX!G$f0Fq~{#yUnGjB-O#1BaTkH?w7>I;R8dgze&?m%EJRIR|XmzK!& z(|9VJo-L2hm$muv*CxxF-VD{XK~1+8b=B)3cnwXy)8Ys^y>3wQIiX z7=d*&hzd2Not>F2pPQHtOEuWeuBgqH&z5J))2GXGtx~L5fw^b~CRLHuu&B*UYiG)n zWnyr8eD3u4nKBExc;O7A(hTX&AYSCIoEmI5lZ95ir{yzhu_(UxPKYZ!eq*#s%fHL> zO5-E9rn@y&)KjreePw}siSAXy3`l_s^}zBIl}tuqn@r2*QewRA8+FINUMKrmaNVk9 zIvv{8oqDzE8;%?OyjxQ-uqCbe4QTGOGsA)%eT zL2c-s(9YVDJz951iIOEcJCs44OOHAS{`O*bL*&HyHW)>1rWJOl-<3Vropi*tTIpu8 z(u#I2t=_&Q3Ssm0uUGBBGR%75Hkxx({coV2pE^Gp0@^oqR3dIL z36XLyLrIW@fkcgQYX)+8M_OH(Sl>Vr*moL6LNxnTr!1SZtnSKY)PVuuL>BMq}XWH59RthK)H% zgTN#2YCZ1e8B+Ubj3p`|~Y$6(3W>QOH z!{3&ecBaZBjbgS&^iH`yqaHgZ-aQ!-jinX~Nbx*l@edj2#&BuH3#qhv^r(0_tVMI= z^(~rxIu)zyZ9i9ouE;@VAVYL#@zuVxIyc;_tM)DPdevC5m%Olr_(rKaM7pzI%&5b| z;+;1eh3dMaTfUDgmfjs5a=l@BjhSrsimbDXCL=RzwW@6`QtwFpoxMbp$7t@Y74zvM z!VdAoVK_J7>@{R0_QRmHoFG0?g}%hB-PQ#|A_ zEF^2y+{$udKiG*jwmy!!#5HMVl+{ti{270m&NhL$LSgrL2=_7fyRjVI}u(_*(bvUexl-7h4VM~6k+FssZhwZ7Xi%8)mS z*07;>#Bagp7$Ici4xd95wK=0$-Ld>8x3sjGhS;4!7Yja{2HLG;qZMhUe&)08 z1Klk@)o@Of8wP6n6_Xl`vE;e+T7`Reu*)pD$gNpq)ujD}zk zcbAx_D9Lf7lY8GK-~EdH)nxxaZ%BMzycQDXNeLRCD>wJdV*5!nQGY`X``-8ZLyono z$(;9o5WRSsrx%594O~Kj`F7Ua-lM)W8l~EJ+mWn@+)&scPfDR}m!NZ?*R`uv*QAwX zQ#U@XXsWE`$M;=$?mddS6$NZ~KQF2?pUW0UZmswPtti>~e@XcRLHP&eoAiVS2!H?x zfB*=900@8p2!H?xfB*=9z=jEYNy^ek@ux&_?=?E^7Z?@OTeKZ=kOa3zM@EkimrBE< zBlDxBQzu4Fl}>(sbrwTlvLfS8x;v1V8`;KmY_l z00ck)1V8`;KmY_lV6z1JB}o*ezEqzi%2G;K;h%5T?6644+4 z0w4eaAOHd&00JNY0w4eaAOHf}k3c`2AQ0qiWBvadLHW)0s{%?40w4eaAOHd&00JNY z0w4eaAOHd&ur`6Tn5Gi~^!fkHUkS1}CCK|Tf7}1#%(c|}(ig<3zF!r-)wrLc@z^MV z)#nE?>ak@^waK!k$K$oZj!^4n z&9i);eVoHy)HT})tR>6Srf250>5G$-Lz=$ix%FC)$P2UQrp9M4Yv;?CwZV2)e4?Fc z6o<64GqdG$6Vq$oQ`Bb5XUntY>C@%8#M0`N9~HQ$%}i@&%9CZP@agfn)8l8#Eac*a zGYmm9q&v^3Mtm+6)32C|mT!ddo5?^cj%#W4;xSRMoki=Wf4yqc=@_#fxZ!&vrURoC z3wr+U!gCpQY)riO7O&_w%;=tW(BU4+Zc9)-G)5|>TCG!g;}_>=&P`KdQ|0OTPKCv8 zqsXhaLpDv8(lvj@tX8`O7R|uawQD}vTwvYoyqC9) zN1&&>r=HEI!^7gemqN%@cPVU4HP5YCUT~}Xy~gA1j(!AeA7^;0%ApY6{nt5ot)8ew zr}h51WO%LU5MJw^w!QY~X*SUfC8ybG)UjTuho!|++DxzTf6(o``b=7#8}8NZw2I#% zZj`!126nzLWZ6h`*EpC_OC|B1&8fDnme!r^kY|(C+ZK2Ptz#SaJolxiGwP8e;vbz0 z2{$b6ZQF4yuk-aGo@!^*BjA}ZqV#Hi?5t<6IIHGXmU~1#;x4_rk@ct%Khh@%eU^sZ zLerH=)pQz%Vn4%FB_hkGZQZcg*gL3q#BX3Cq!sChxl?MfDBdfFR>)6z_;&|+OXMTB zW{Xubu*ixUR^KrrX-qV2$Dna|iA_goTH(4?%XB*D6}E4vrA@d>3?Tzx~$r~?Dyd;KBDT1(f_i>G;dQTW!tB^0ka1%qp8kuE)JcQMaOi4NvGhT0!Tsp+_sZ z`<27timH2}-I@eOHE0FA(51`P608;f$aVVACWTSe+M_hHqlc;-t=QQA|3pwe+2|q{ zav%T#AOHd&00JNY0w4eaAOHd&00NH>ffv{ge_ysda#DZiiq4z}SM*cb6}_>|N!zE+ z75%o}SpPo|ln0Mb$x#vz009sH0T2KI5C8!X009sH0T2LzhekjWC0X7<$%(Z1$NK-F zaYaHP00JNY0w4eaAOHd&00JNY0wC}>5lE%j=l`ihqVlf-{ow%uAOHd&00JNY0w4ea zAOHd&00JPe4GHM<75{Q}f*t>Fef>elM*ytF_W6H&{(l>)7exjE5C8!X009sH0T2KI z5C8!X00AO^&;P>=KmY_l00ck)1V8`;KmY_l00ck)1O$BkA4UKIAOHd&00JNY0w4ea zAOHd&00K`wf$q=$KTlQG={ii8M%Msc&(ZZPUC+>Uh^~WlJx$kBbg6V5pld%}`{>$B z*B-idH!p>r3v{us7|7AJlP>mkgEC$0hm13HrRhr1g$D?L00@8p2!H?xfB*=900@8p O2!OyA32fx^|Njf=37J^{ diff --git a/optd-persistent/src/entities/attribute.rs b/optd-persistent/src/entities/attribute.rs deleted file mode 100644 index aa312c0..0000000 --- a/optd-persistent/src/entities/attribute.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "attribute")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub table_id: i32, - pub name: String, - pub compression_method: String, - pub variant_tag: i32, - pub base_attribute_number: i32, - pub is_not_null: bool, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm(has_many = "super::attribute_constraint_junction::Entity")] - AttributeConstraintJunction, - #[sea_orm(has_many = "super::attribute_foreign_constraint_junction::Entity")] - AttributeForeignConstraintJunction, - #[sea_orm(has_many = "super::statistic_to_attribute_junction::Entity")] - StatisticToAttributeJunction, - #[sea_orm( - belongs_to = "super::table_metadata::Entity", - from = "Column::TableId", - to = "super::table_metadata::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - TableMetadata, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::AttributeConstraintJunction.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::AttributeForeignConstraintJunction.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::StatisticToAttributeJunction.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::TableMetadata.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - super::statistic_to_attribute_junction::Relation::Statistic.def() - } - fn via() -> Option { - Some( - super::statistic_to_attribute_junction::Relation::Attribute - .def() - .rev(), - ) - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/attribute_constraint_junction.rs b/optd-persistent/src/entities/attribute_constraint_junction.rs deleted file mode 100644 index 7c61c72..0000000 --- a/optd-persistent/src/entities/attribute_constraint_junction.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "attribute_constraint_junction")] -pub struct Model { - #[sea_orm(primary_key, auto_increment = false)] - pub attribute_id: i32, - #[sea_orm(primary_key, auto_increment = false)] - pub constraint_id: i32, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::attribute::Entity", - from = "Column::AttributeId", - to = "super::attribute::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - Attribute, - #[sea_orm( - belongs_to = "super::constraint_metadata::Entity", - from = "Column::ConstraintId", - to = "super::constraint_metadata::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - ConstraintMetadata, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Attribute.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::ConstraintMetadata.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/attribute_foreign_constraint_junction.rs b/optd-persistent/src/entities/attribute_foreign_constraint_junction.rs deleted file mode 100644 index f219a39..0000000 --- a/optd-persistent/src/entities/attribute_foreign_constraint_junction.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "attribute_foreign_constraint_junction")] -pub struct Model { - #[sea_orm(primary_key, auto_increment = false)] - pub attribute_id: i32, - #[sea_orm(primary_key, auto_increment = false)] - pub constraint_id: i32, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::attribute::Entity", - from = "Column::AttributeId", - to = "super::attribute::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - Attribute, - #[sea_orm( - belongs_to = "super::constraint_metadata::Entity", - from = "Column::ConstraintId", - to = "super::constraint_metadata::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - ConstraintMetadata, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Attribute.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::ConstraintMetadata.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/cascades_group.rs b/optd-persistent/src/entities/cascades_group.rs deleted file mode 100644 index 9ea79d1..0000000 --- a/optd-persistent/src/entities/cascades_group.rs +++ /dev/null @@ -1,83 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "cascades_group")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub latest_winner: Option, - pub in_progress: bool, - pub is_optimized: bool, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm(has_many = "super::group_winner::Entity")] - GroupWinner, - #[sea_orm(has_many = "super::logical_children::Entity")] - LogicalChildren, - #[sea_orm(has_many = "super::logical_expression::Entity")] - LogicalExpression, - #[sea_orm(has_many = "super::logical_property::Entity")] - LogicalProperty, - #[sea_orm(has_many = "super::physical_children::Entity")] - PhysicalChildren, - #[sea_orm( - belongs_to = "super::physical_expression::Entity", - from = "Column::LatestWinner", - to = "super::physical_expression::Column::Id", - on_update = "Cascade", - on_delete = "SetNull" - )] - PhysicalExpression, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::GroupWinner.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::LogicalChildren.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::LogicalProperty.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::PhysicalChildren.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - super::logical_children::Relation::LogicalExpression.def() - } - fn via() -> Option { - Some(super::logical_children::Relation::CascadesGroup.def().rev()) - } -} - -impl Related for Entity { - fn to() -> RelationDef { - super::physical_children::Relation::PhysicalExpression.def() - } - fn via() -> Option { - Some( - super::physical_children::Relation::CascadesGroup - .def() - .rev(), - ) - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/constraint_metadata.rs b/optd-persistent/src/entities/constraint_metadata.rs deleted file mode 100644 index 8adf1d7..0000000 --- a/optd-persistent/src/entities/constraint_metadata.rs +++ /dev/null @@ -1,68 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "constraint_metadata")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub name: String, - pub variant_tag: i32, - pub table_id: Option, - pub index_id: Option, - pub foreign_ref_id: Option, - pub check_src: String, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm(has_many = "super::attribute_constraint_junction::Entity")] - AttributeConstraintJunction, - #[sea_orm(has_many = "super::attribute_foreign_constraint_junction::Entity")] - AttributeForeignConstraintJunction, - #[sea_orm( - belongs_to = "super::index_metadata::Entity", - from = "Column::IndexId", - to = "super::index_metadata::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - IndexMetadata, - #[sea_orm( - belongs_to = "super::table_metadata::Entity", - from = "Column::ForeignRefId", - to = "super::table_metadata::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - TableMetadata2, - #[sea_orm( - belongs_to = "super::table_metadata::Entity", - from = "Column::TableId", - to = "super::table_metadata::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - TableMetadata1, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::AttributeConstraintJunction.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::AttributeForeignConstraintJunction.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::IndexMetadata.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/database_metadata.rs b/optd-persistent/src/entities/database_metadata.rs deleted file mode 100644 index 98106d6..0000000 --- a/optd-persistent/src/entities/database_metadata.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "database_metadata")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub name: String, - pub creation_time: DateTimeUtc, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm(has_many = "super::namespace_metadata::Entity")] - NamespaceMetadata, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::NamespaceMetadata.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/event.rs b/optd-persistent/src/entities/event.rs deleted file mode 100644 index ad9157d..0000000 --- a/optd-persistent/src/entities/event.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "event")] -pub struct Model { - #[sea_orm(primary_key)] - pub epoch_id: i32, - pub timestamp: DateTimeUtc, - pub source_variant: String, - pub data: Json, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm(has_many = "super::group_winner::Entity")] - GroupWinner, - #[sea_orm(has_many = "super::plan_cost::Entity")] - PlanCost, - #[sea_orm(has_many = "super::versioned_statistic::Entity")] - VersionedStatistic, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::GroupWinner.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::PlanCost.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::VersionedStatistic.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/group_winner.rs b/optd-persistent/src/entities/group_winner.rs deleted file mode 100644 index 32cbcb4..0000000 --- a/optd-persistent/src/entities/group_winner.rs +++ /dev/null @@ -1,76 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "group_winner")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub group_id: i32, - pub physical_expression_id: i32, - pub cost_id: i32, - pub epoch_id: i32, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::cascades_group::Entity", - from = "Column::GroupId", - to = "super::cascades_group::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - CascadesGroup, - #[sea_orm( - belongs_to = "super::event::Entity", - from = "Column::EpochId", - to = "super::event::Column::EpochId", - on_update = "Cascade", - on_delete = "Cascade" - )] - Event, - #[sea_orm( - belongs_to = "super::physical_expression::Entity", - from = "Column::PhysicalExpressionId", - to = "super::physical_expression::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - PhysicalExpression, - #[sea_orm( - belongs_to = "super::plan_cost::Entity", - from = "Column::CostId", - to = "super::plan_cost::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - PlanCost, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::CascadesGroup.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Event.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::PhysicalExpression.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::PlanCost.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/index_metadata.rs b/optd-persistent/src/entities/index_metadata.rs deleted file mode 100644 index 47263b7..0000000 --- a/optd-persistent/src/entities/index_metadata.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "index_metadata")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub table_id: i32, - pub name: String, - pub number_of_attributes: i32, - pub variant_tag: i32, - pub is_unique: bool, - pub nulls_not_distinct: bool, - pub is_primary: bool, - pub is_clustered: bool, - pub is_exclusion: bool, - pub description: String, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm(has_many = "super::constraint_metadata::Entity")] - ConstraintMetadata, - #[sea_orm( - belongs_to = "super::table_metadata::Entity", - from = "Column::TableId", - to = "super::table_metadata::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - TableMetadata, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::ConstraintMetadata.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::TableMetadata.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/logical_children.rs b/optd-persistent/src/entities/logical_children.rs deleted file mode 100644 index 120641f..0000000 --- a/optd-persistent/src/entities/logical_children.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "logical_children")] -pub struct Model { - #[sea_orm(primary_key, auto_increment = false)] - pub logical_expression_id: i32, - #[sea_orm(primary_key, auto_increment = false)] - pub group_id: i32, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::cascades_group::Entity", - from = "Column::GroupId", - to = "super::cascades_group::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - CascadesGroup, - #[sea_orm( - belongs_to = "super::logical_expression::Entity", - from = "Column::GroupId", - to = "super::logical_expression::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - LogicalExpression, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::CascadesGroup.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::LogicalExpression.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/logical_expression.rs b/optd-persistent/src/entities/logical_expression.rs deleted file mode 100644 index 6beff04..0000000 --- a/optd-persistent/src/entities/logical_expression.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "logical_expression")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub group_id: i32, - pub fingerprint: i64, - pub variant_tag: i16, - pub data: Json, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::cascades_group::Entity", - from = "Column::GroupId", - to = "super::cascades_group::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - CascadesGroup, - #[sea_orm(has_many = "super::logical_children::Entity")] - LogicalChildren, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::LogicalChildren.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - super::logical_children::Relation::CascadesGroup.def() - } - fn via() -> Option { - Some( - super::logical_children::Relation::LogicalExpression - .def() - .rev(), - ) - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/logical_property.rs b/optd-persistent/src/entities/logical_property.rs deleted file mode 100644 index 755575c..0000000 --- a/optd-persistent/src/entities/logical_property.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "logical_property")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub group_id: i32, - pub variant_tag: i16, - pub data: Json, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::cascades_group::Entity", - from = "Column::GroupId", - to = "super::cascades_group::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - CascadesGroup, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::CascadesGroup.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/mod.rs b/optd-persistent/src/entities/mod.rs deleted file mode 100644 index cd2d9d0..0000000 --- a/optd-persistent/src/entities/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -pub mod prelude; - -pub mod attribute; -pub mod attribute_constraint_junction; -pub mod attribute_foreign_constraint_junction; -pub mod cascades_group; -pub mod constraint_metadata; -pub mod database_metadata; -pub mod event; -pub mod group_winner; -pub mod index_metadata; -pub mod logical_children; -pub mod logical_expression; -pub mod logical_property; -pub mod namespace_metadata; -pub mod physical_children; -pub mod physical_expression; -pub mod physical_expression_to_statistic_junction; -pub mod physical_property; -pub mod plan_cost; -pub mod predicate; -pub mod predicate_children; -pub mod predicate_logical_expression_junction; -pub mod predicate_physical_expression_junction; -pub mod statistic; -pub mod statistic_to_attribute_junction; -pub mod table_metadata; -pub mod trigger; -pub mod versioned_statistic; diff --git a/optd-persistent/src/entities/namespace_metadata.rs b/optd-persistent/src/entities/namespace_metadata.rs deleted file mode 100644 index 8f63146..0000000 --- a/optd-persistent/src/entities/namespace_metadata.rs +++ /dev/null @@ -1,41 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "namespace_metadata")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub name: String, - pub database_id: i32, - pub creation_time: DateTimeUtc, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::database_metadata::Entity", - from = "Column::DatabaseId", - to = "super::database_metadata::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - DatabaseMetadata, - #[sea_orm(has_many = "super::table_metadata::Entity")] - TableMetadata, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::DatabaseMetadata.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::TableMetadata.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/physical_children.rs b/optd-persistent/src/entities/physical_children.rs deleted file mode 100644 index d8f9db0..0000000 --- a/optd-persistent/src/entities/physical_children.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "physical_children")] -pub struct Model { - #[sea_orm(primary_key, auto_increment = false)] - pub physical_expression_id: i32, - #[sea_orm(primary_key, auto_increment = false)] - pub group_id: i32, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::cascades_group::Entity", - from = "Column::GroupId", - to = "super::cascades_group::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - CascadesGroup, - #[sea_orm( - belongs_to = "super::physical_expression::Entity", - from = "Column::PhysicalExpressionId", - to = "super::physical_expression::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - PhysicalExpression, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::CascadesGroup.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::PhysicalExpression.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/physical_expression.rs b/optd-persistent/src/entities/physical_expression.rs deleted file mode 100644 index 4876d35..0000000 --- a/optd-persistent/src/entities/physical_expression.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "physical_expression")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub group_id: i32, - pub fingerprint: i64, - pub variant_tag: i16, - pub data: Json, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::cascades_group::Entity", - from = "Column::GroupId", - to = "super::cascades_group::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - CascadesGroup, - #[sea_orm(has_many = "super::group_winner::Entity")] - GroupWinner, - #[sea_orm(has_many = "super::physical_children::Entity")] - PhysicalChildren, - #[sea_orm(has_many = "super::physical_expression_to_statistic_junction::Entity")] - PhysicalExpressionToStatisticJunction, - #[sea_orm(has_many = "super::physical_property::Entity")] - PhysicalProperty, - #[sea_orm(has_many = "super::plan_cost::Entity")] - PlanCost, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::GroupWinner.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::PhysicalChildren.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::PhysicalExpressionToStatisticJunction.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::PhysicalProperty.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::PlanCost.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - super::physical_children::Relation::CascadesGroup.def() - } - fn via() -> Option { - Some( - super::physical_children::Relation::PhysicalExpression - .def() - .rev(), - ) - } -} - -impl Related for Entity { - fn to() -> RelationDef { - super::physical_expression_to_statistic_junction::Relation::Statistic.def() - } - fn via() -> Option { - Some( - super::physical_expression_to_statistic_junction::Relation::PhysicalExpression - .def() - .rev(), - ) - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/physical_expression_to_statistic_junction.rs b/optd-persistent/src/entities/physical_expression_to_statistic_junction.rs deleted file mode 100644 index f8020bd..0000000 --- a/optd-persistent/src/entities/physical_expression_to_statistic_junction.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "physical_expression_to_statistic_junction")] -pub struct Model { - #[sea_orm(primary_key, auto_increment = false)] - pub physical_expression_id: i32, - #[sea_orm(primary_key, auto_increment = false)] - pub statistic_id: i32, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::physical_expression::Entity", - from = "Column::PhysicalExpressionId", - to = "super::physical_expression::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - PhysicalExpression, - #[sea_orm( - belongs_to = "super::statistic::Entity", - from = "Column::StatisticId", - to = "super::statistic::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - Statistic, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::PhysicalExpression.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Statistic.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/physical_property.rs b/optd-persistent/src/entities/physical_property.rs deleted file mode 100644 index 51d98dc..0000000 --- a/optd-persistent/src/entities/physical_property.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "physical_property")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub physical_expression_id: i32, - pub variant_tag: i16, - pub data: Json, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::physical_expression::Entity", - from = "Column::PhysicalExpressionId", - to = "super::physical_expression::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - PhysicalExpression, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::PhysicalExpression.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/plan_cost.rs b/optd-persistent/src/entities/plan_cost.rs deleted file mode 100644 index 5d7e24b..0000000 --- a/optd-persistent/src/entities/plan_cost.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] -#[sea_orm(table_name = "plan_cost")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub physical_expression_id: i32, - pub epoch_id: i32, - pub cost: Option, - #[sea_orm(column_type = "Float", nullable)] - pub estimated_statistic: Option, - pub is_valid: bool, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::event::Entity", - from = "Column::EpochId", - to = "super::event::Column::EpochId", - on_update = "Cascade", - on_delete = "Cascade" - )] - Event, - #[sea_orm(has_many = "super::group_winner::Entity")] - GroupWinner, - #[sea_orm( - belongs_to = "super::physical_expression::Entity", - from = "Column::PhysicalExpressionId", - to = "super::physical_expression::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - PhysicalExpression, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Event.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::GroupWinner.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::PhysicalExpression.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/predicate.rs b/optd-persistent/src/entities/predicate.rs deleted file mode 100644 index e142a21..0000000 --- a/optd-persistent/src/entities/predicate.rs +++ /dev/null @@ -1,60 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.1 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "predicate")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub data: Json, - pub variant: i32, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm(has_many = "super::predicate_logical_expression_junction::Entity")] - PredicateLogicalExpressionJunction, - #[sea_orm(has_many = "super::predicate_physical_expression_junction::Entity")] - PredicatePhysicalExpressionJunction, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::PredicateLogicalExpressionJunction.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::PredicatePhysicalExpressionJunction.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - super::predicate_logical_expression_junction::Relation::LogicalExpression.def() - } - fn via() -> Option { - Some( - super::predicate_logical_expression_junction::Relation::Predicate - .def() - .rev(), - ) - } -} - -impl Related for Entity { - fn to() -> RelationDef { - super::predicate_physical_expression_junction::Relation::PhysicalExpression.def() - } - fn via() -> Option { - Some( - super::predicate_physical_expression_junction::Relation::Predicate - .def() - .rev(), - ) - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/predicate_children.rs b/optd-persistent/src/entities/predicate_children.rs deleted file mode 100644 index 93ef3eb..0000000 --- a/optd-persistent/src/entities/predicate_children.rs +++ /dev/null @@ -1,34 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.1 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "predicate_children")] -pub struct Model { - #[sea_orm(primary_key, auto_increment = false)] - pub parent_id: i32, - #[sea_orm(primary_key, auto_increment = false)] - pub child_id: i32, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::predicate::Entity", - from = "Column::ChildId", - to = "super::predicate::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - Predicate2, - #[sea_orm( - belongs_to = "super::predicate::Entity", - from = "Column::ParentId", - to = "super::predicate::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - Predicate1, -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/predicate_logical_expression_junction.rs b/optd-persistent/src/entities/predicate_logical_expression_junction.rs deleted file mode 100644 index 520da03..0000000 --- a/optd-persistent/src/entities/predicate_logical_expression_junction.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.1 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "predicate_logical_expression_junction")] -pub struct Model { - #[sea_orm(primary_key, auto_increment = false)] - pub logical_expr_id: i32, - #[sea_orm(primary_key, auto_increment = false)] - pub predicate_id: i32, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::logical_expression::Entity", - from = "Column::LogicalExprId", - to = "super::logical_expression::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - LogicalExpression, - #[sea_orm( - belongs_to = "super::predicate::Entity", - from = "Column::PredicateId", - to = "super::predicate::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - Predicate, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::LogicalExpression.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Predicate.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/predicate_physical_expression_junction.rs b/optd-persistent/src/entities/predicate_physical_expression_junction.rs deleted file mode 100644 index 263abc8..0000000 --- a/optd-persistent/src/entities/predicate_physical_expression_junction.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.1 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "predicate_physical_expression_junction")] -pub struct Model { - #[sea_orm(primary_key, auto_increment = false)] - pub physical_expr_id: i32, - #[sea_orm(primary_key, auto_increment = false)] - pub predicate_id: i32, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::physical_expression::Entity", - from = "Column::PhysicalExprId", - to = "super::physical_expression::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - PhysicalExpression, - #[sea_orm( - belongs_to = "super::predicate::Entity", - from = "Column::PredicateId", - to = "super::predicate::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - Predicate, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::PhysicalExpression.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Predicate.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/prelude.rs b/optd-persistent/src/entities/prelude.rs deleted file mode 100644 index 753e58b..0000000 --- a/optd-persistent/src/entities/prelude.rs +++ /dev/null @@ -1,25 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -pub use super::attribute::Entity as Attribute; -pub use super::attribute_constraint_junction::Entity as AttributeConstraintJunction; -pub use super::attribute_foreign_constraint_junction::Entity as AttributeForeignConstraintJunction; -pub use super::cascades_group::Entity as CascadesGroup; -pub use super::constraint_metadata::Entity as ConstraintMetadata; -pub use super::database_metadata::Entity as DatabaseMetadata; -pub use super::event::Entity as Event; -pub use super::group_winner::Entity as GroupWinner; -pub use super::index_metadata::Entity as IndexMetadata; -pub use super::logical_children::Entity as LogicalChildren; -pub use super::logical_expression::Entity as LogicalExpression; -pub use super::logical_property::Entity as LogicalProperty; -pub use super::namespace_metadata::Entity as NamespaceMetadata; -pub use super::physical_children::Entity as PhysicalChildren; -pub use super::physical_expression::Entity as PhysicalExpression; -pub use super::physical_expression_to_statistic_junction::Entity as PhysicalExpressionToStatisticJunction; -pub use super::physical_property::Entity as PhysicalProperty; -pub use super::plan_cost::Entity as PlanCost; -pub use super::statistic::Entity as Statistic; -pub use super::statistic_to_attribute_junction::Entity as StatisticToAttributeJunction; -pub use super::table_metadata::Entity as TableMetadata; -pub use super::trigger::Entity as Trigger; -pub use super::versioned_statistic::Entity as VersionedStatistic; diff --git a/optd-persistent/src/entities/statistic.rs b/optd-persistent/src/entities/statistic.rs deleted file mode 100644 index 1eaf96b..0000000 --- a/optd-persistent/src/entities/statistic.rs +++ /dev/null @@ -1,86 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "statistic")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub name: String, - pub table_id: Option, - pub creation_time: DateTimeUtc, - pub number_of_attributes: i32, - pub variant_tag: i32, - pub description: String, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm(has_many = "super::physical_expression_to_statistic_junction::Entity")] - PhysicalExpressionToStatisticJunction, - #[sea_orm(has_many = "super::statistic_to_attribute_junction::Entity")] - StatisticToAttributeJunction, - #[sea_orm( - belongs_to = "super::table_metadata::Entity", - from = "Column::TableId", - to = "super::table_metadata::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - TableMetadata, - #[sea_orm(has_many = "super::versioned_statistic::Entity")] - VersionedStatistic, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::PhysicalExpressionToStatisticJunction.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::StatisticToAttributeJunction.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::TableMetadata.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::VersionedStatistic.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - super::statistic_to_attribute_junction::Relation::Attribute.def() - } - fn via() -> Option { - Some( - super::statistic_to_attribute_junction::Relation::Statistic - .def() - .rev(), - ) - } -} - -impl Related for Entity { - fn to() -> RelationDef { - super::physical_expression_to_statistic_junction::Relation::PhysicalExpression.def() - } - fn via() -> Option { - Some( - super::physical_expression_to_statistic_junction::Relation::Statistic - .def() - .rev(), - ) - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/statistic_to_attribute_junction.rs b/optd-persistent/src/entities/statistic_to_attribute_junction.rs deleted file mode 100644 index 63d23fb..0000000 --- a/optd-persistent/src/entities/statistic_to_attribute_junction.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "statistic_to_attribute_junction")] -pub struct Model { - #[sea_orm(primary_key, auto_increment = false)] - pub statistic_id: i32, - #[sea_orm(primary_key, auto_increment = false)] - pub attribute_id: i32, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::attribute::Entity", - from = "Column::AttributeId", - to = "super::attribute::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - Attribute, - #[sea_orm( - belongs_to = "super::statistic::Entity", - from = "Column::StatisticId", - to = "super::statistic::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - Statistic, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Attribute.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Statistic.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/table_metadata.rs b/optd-persistent/src/entities/table_metadata.rs deleted file mode 100644 index 35ea923..0000000 --- a/optd-persistent/src/entities/table_metadata.rs +++ /dev/null @@ -1,65 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "table_metadata")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub name: String, - pub namespace_id: i32, - pub creation_time: DateTimeUtc, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm(has_many = "super::attribute::Entity")] - Attribute, - #[sea_orm(has_many = "super::index_metadata::Entity")] - IndexMetadata, - #[sea_orm( - belongs_to = "super::namespace_metadata::Entity", - from = "Column::NamespaceId", - to = "super::namespace_metadata::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - NamespaceMetadata, - #[sea_orm(has_many = "super::statistic::Entity")] - Statistic, - #[sea_orm(has_many = "super::trigger::Entity")] - Trigger, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Attribute.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::IndexMetadata.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::NamespaceMetadata.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Statistic.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Trigger.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/trigger.rs b/optd-persistent/src/entities/trigger.rs deleted file mode 100644 index a5f9de8..0000000 --- a/optd-persistent/src/entities/trigger.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "trigger")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub name: String, - pub table_id: i32, - pub parent_trigger_id: i32, - pub function: Json, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::table_metadata::Entity", - from = "Column::TableId", - to = "super::table_metadata::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - TableMetadata, - #[sea_orm( - belongs_to = "Entity", - from = "Column::ParentTriggerId", - to = "Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - SelfRef, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::TableMetadata.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/entities/versioned_statistic.rs b/optd-persistent/src/entities/versioned_statistic.rs deleted file mode 100644 index c4533bf..0000000 --- a/optd-persistent/src/entities/versioned_statistic.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 - -use sea_orm::entity::prelude::*; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "versioned_statistic")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: i32, - pub epoch_id: i32, - pub statistic_id: i32, - pub statistic_value: Json, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::event::Entity", - from = "Column::EpochId", - to = "super::event::Column::EpochId", - on_update = "Cascade", - on_delete = "Cascade" - )] - Event, - #[sea_orm( - belongs_to = "super::statistic::Entity", - from = "Column::StatisticId", - to = "super::statistic::Column::Id", - on_update = "Cascade", - on_delete = "Cascade" - )] - Statistic, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Event.def() - } -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Statistic.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-persistent/src/lib.rs b/optd-persistent/src/lib.rs deleted file mode 100644 index dd95da2..0000000 --- a/optd-persistent/src/lib.rs +++ /dev/null @@ -1,114 +0,0 @@ -#![allow(dead_code)] - -use std::sync::LazyLock; - -use sea_orm::*; -use sea_orm_migration::prelude::*; - -use migrator::Migrator; - -pub mod entities; -mod migrator; - -pub mod cost_model; -pub use cost_model::interface::CostModelStorageLayer; - -mod memo; -pub use memo::interface::Memo; - -/// The filename of the SQLite database for migration. -pub const DATABASE_FILENAME: &str = "sqlite.db"; -/// The URL of the SQLite database for migration. -pub const DATABASE_URL: &str = "sqlite:./sqlite.db?mode=rwc"; - -/// The filename of the SQLite database for testing. -pub const TEST_DATABASE_FILENAME: &str = "init.db"; -/// The URL of the SQLite database for testing. -pub static TEST_DATABASE_FILE: LazyLock = LazyLock::new(|| { - std::env::current_dir() - .unwrap() - .join("src") - .join("db") - .join(TEST_DATABASE_FILENAME) - .to_str() - .unwrap() - .to_owned() -}); -/// The URL of the SQLite database for testing. -pub static TEST_DATABASE_URL: LazyLock = - LazyLock::new(|| get_sqlite_url(http://github.com/TEST_DATABASE_FILE.as_str())); - -fn get_sqlite_url(/service/file:///%20&str) -> String { - format!("sqlite:{}?mode=rwc", file) -} - -#[derive(Debug)] -pub enum CostModelError { - // TODO: Add more error types - UnknownStatisticType, - VersionedStatisticNotFound, - CustomError(String), -} - -/// TODO convert this to `thiserror` -#[derive(Debug)] -/// The different kinds of errors that might occur while running operations on a memo table. -pub enum MemoError { - UnknownGroup, - UnknownLogicalExpression, - UnknownPhysicalExpression, - InvalidExpression, -} - -/// TODO convert this to `thiserror` -#[derive(Debug)] -pub enum BackendError { - Memo(MemoError), - DatabaseError(DbErr), - CostModel(CostModelError), - BackendError(String), -} - -impl From for CostModelError { - fn from(value: String) -> Self { - CostModelError::CustomError(value) - } -} - -impl From for BackendError { - fn from(value: CostModelError) -> Self { - BackendError::CostModel(value) - } -} - -impl From for BackendError { - fn from(value: MemoError) -> Self { - BackendError::Memo(value) - } -} - -impl From for BackendError { - fn from(value: DbErr) -> Self { - BackendError::DatabaseError(value) - } -} - -/// A type alias for a result with [`BackendError`] as the error type. -pub type StorageResult = Result; - -pub struct BackendManager { - db: DatabaseConnection, -} - -impl BackendManager { - /// Creates a new `BackendManager`. - pub async fn new(database_url: Option<&str>) -> StorageResult { - Ok(Self { - db: Database::connect(database_url.unwrap_or(DATABASE_URL)).await?, - }) - } -} - -pub async fn migrate(db: &DatabaseConnection) -> Result<(), DbErr> { - Migrator::refresh(db).await -} diff --git a/optd-persistent/src/main.rs b/optd-persistent/src/main.rs deleted file mode 100644 index 165189e..0000000 --- a/optd-persistent/src/main.rs +++ /dev/null @@ -1,112 +0,0 @@ -//! Very basic demo of using the ORM for optd-persistent. -//! -//! You may run into errors when you first clone this repository. -//! See the `README.md` for setup instructions. - -#![allow(dead_code, unused_imports)] - -use sea_orm::*; -use sea_orm_migration::prelude::*; -use serde_json::json; - -mod entities; -mod migrator; - -use entities::{prelude::*, *}; -use optd_persistent::DATABASE_URL; - -#[tokio::main] -async fn main() { - basic_demo().await; - memo_demo().await; -} - -async fn memo_demo() { - let _db = Database::connect(DATABASE_URL).await.unwrap(); - - todo!() -} - -async fn basic_demo() { - let db = Database::connect(DATABASE_URL).await.unwrap(); - - // Create a new `CascadesGroup`. - let group = cascades_group::ActiveModel { - latest_winner: ActiveValue::Set(None), - in_progress: ActiveValue::Set(false), - is_optimized: ActiveValue::Set(false), - ..Default::default() - } - .save(&db) - .await - .unwrap(); - - // Create a new logical expression. - let l_expr = logical_expression::ActiveModel { - group_id: group.id.clone(), - fingerprint: ActiveValue::Set(42), // Example fingerprint - variant_tag: ActiveValue::Set(1), // Example variant tag - data: ActiveValue::Set(json!({ // Example operator - "type": "Scan", - "table": "lineitem", - "predicate": "l_quantity < 10", - })), - ..Default::default() - } - .save(&db) - .await - .unwrap(); - - // Create a link between the group and the logical expression in the junction table. - let _link = logical_children::ActiveModel { - group_id: group.id.clone(), - logical_expression_id: l_expr.id.clone(), - } - .insert(&db) - .await - .unwrap(); - - // Basic lookup test on each table. - { - let groups: Vec = CascadesGroup::find().all(&db).await.unwrap(); - assert_eq!(groups.len(), 1); - - let l_expressions: Vec = - LogicalExpression::find().all(&db).await.unwrap(); - assert_eq!(l_expressions.len(), 1); - } - - // Retrieve all logical expressions that belong to this group with lazy loading. - { - let group = CascadesGroup::find_by_id(*group.id.try_as_ref().unwrap()) - .one(&db) - .await - .unwrap() - .unwrap(); - - let group_expressions: Vec = group - .find_related(LogicalExpression) - .all(&db) - .await - .unwrap(); - assert_eq!(group_expressions.len(), 1); - } - - // Retrieve all logical expressions that belong to this group with eager loading. - { - let group_with_expressions: Vec<(cascades_group::Model, Vec)> = - CascadesGroup::find() - .find_with_related(LogicalExpression) - .all(&db) - .await - .unwrap(); - assert_eq!(group_with_expressions.len(), 1); - assert_eq!(group_with_expressions[0].1.len(), 1); - } - - // Clean up everything. Since everything is cascading, we only need to manually delete the group - // and then SeaORM will take care of the expression and the junction. - group.delete(&db).await.unwrap(); - - println!("Demo Finished!"); -} diff --git a/optd-persistent/src/memo/expression.rs b/optd-persistent/src/memo/expression.rs deleted file mode 100644 index ff1590c..0000000 --- a/optd-persistent/src/memo/expression.rs +++ /dev/null @@ -1,73 +0,0 @@ -use crate::entities::*; -use std::hash::{DefaultHasher, Hash, Hasher}; - -/// All of the different types of fixed logical operators. -/// -/// Note that there could be more operators that the memo table must support that are not enumerated -/// in this enum, as there can be up to `2^16` different types of operators. -#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] -#[non_exhaustive] -#[repr(i16)] -pub enum LogicalOperator { - Scan, - Join, -} - -/// All of the different types of fixed physical operators. -/// -/// Note that there could be more operators that the memo table must support that are not enumerated -/// in this enum, as there can be up to `2^16` different types of operators. -#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] -#[non_exhaustive] -#[repr(i16)] -pub enum PhysicalOperator { - TableScan, - IndexScan, - NestedLoopJoin, - HashJoin, -} - -/// A method to generate a fingerprint used to efficiently check if two -/// expressions are equivalent. -/// -/// TODO actually make efficient. -fn fingerprint(variant_tag: i16, data: &serde_json::Value) -> i64 { - let mut hasher = DefaultHasher::new(); - - variant_tag.hash(&mut hasher); - data.hash(&mut hasher); - - hasher.finish() as i64 -} - -impl logical_expression::Model { - /// Creates a new logical expression with an unset `id` and `group_id`. - pub fn new(variant_tag: LogicalOperator, data: serde_json::Value) -> Self { - let tag = variant_tag as i16; - let fingerprint = fingerprint(tag, &data); - - Self { - id: 0, - group_id: 0, - fingerprint, - variant_tag: tag, - data, - } - } -} - -impl physical_expression::Model { - /// Creates a new physical expression with an unset `id` and `group_id`. - pub fn new(variant_tag: PhysicalOperator, data: serde_json::Value) -> Self { - let tag = variant_tag as i16; - let fingerprint = fingerprint(tag, &data); - - Self { - id: 0, - group_id: 0, - fingerprint, - variant_tag: tag, - data, - } - } -} diff --git a/optd-persistent/src/memo/interface.rs b/optd-persistent/src/memo/interface.rs deleted file mode 100644 index 2d2240e..0000000 --- a/optd-persistent/src/memo/interface.rs +++ /dev/null @@ -1,141 +0,0 @@ -use crate::StorageResult; - -/// A trait representing an implementation of a memoization table. -/// -/// Note that we use [`trait_variant`] here in order to add bounds on every method. -/// See this [blog post]( -/// https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits.html#async-fn-in-public-traits) -/// for more information. -/// -/// TODO Figure out for each when to get the ID of a record or the entire record itself. -#[trait_variant::make(Send)] -pub trait Memo { - /// A type representing a group in the Cascades framework. - type Group; - /// A type representing a unique identifier for a group. - type GroupId; - /// A type representing a logical expression. - type LogicalExpression; - /// A type representing a unique identifier for a logical expression. - type LogicalExpressionId; - /// A type representing a physical expression. - type PhysicalExpression; - /// A type representing a unique identifier for a physical expression. - type PhysicalExpressionId; - - /// Retrieves a [`Self::Group`] given a [`Self::GroupId`]. - /// - /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. - async fn get_group(&self, group_id: Self::GroupId) -> StorageResult; - - /// Retrieves all group IDs that are stored in the memo table. - async fn get_all_groups(&self) -> StorageResult>; - - /// Retrieves a [`Self::LogicalExpression`] given a [`Self::LogicalExpressionId`]. - /// - /// If the logical expression does not exist, returns a [`MemoError::UnknownLogicalExpression`] - /// error. - async fn get_logical_expression( - &self, - logical_expression_id: Self::LogicalExpressionId, - ) -> StorageResult; - - /// Retrieves a [`Self::PhysicalExpression`] given a [`Self::PhysicalExpressionId`]. - /// - /// If the physical expression does not exist, returns a - /// [`MemoError::UnknownPhysicalExpression`] error. - async fn get_physical_expression( - &self, - physical_expression_id: Self::PhysicalExpressionId, - ) -> StorageResult; - - /// Retrieves the parent group ID of a logical expression given its expression ID. - /// - /// If the logical expression does not exist, returns a [`MemoError::UnknownLogicalExpression`] - /// error. - async fn get_group_from_logical_expression( - &self, - logical_expression_id: Self::LogicalExpressionId, - ) -> StorageResult; - - /// Retrieves the parent group ID of a logical expression given its expression ID. - /// - /// If the physical expression does not exist, returns a - /// [`MemoError::UnknownPhysicalExpression`] error. - async fn get_group_from_physical_expression( - &self, - physical_expression_id: Self::PhysicalExpressionId, - ) -> StorageResult; - - /// Retrieves all of the logical expression "children" of a group. - /// - /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. - async fn get_group_logical_expressions( - &self, - group_id: Self::GroupId, - ) -> StorageResult>; - - /// Retrieves all of the physical expression "children" of a group. - /// - /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. - async fn get_group_physical_expressions( - &self, - group_id: Self::GroupId, - ) -> StorageResult>; - - /// Retrieves the best physical query plan (winner) for a given group. - /// - /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. - async fn get_winner( - &self, - group_id: Self::GroupId, - ) -> StorageResult>; - - /// Updates / replaces a group's best physical plan (winner). Optionally returns the previous - /// winner's physical expression ID. - /// - /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. - async fn update_group_winner( - &self, - group_id: Self::GroupId, - physical_expression_id: Self::PhysicalExpressionId, - ) -> StorageResult>; - - /// Adds a logical expression to an existing group via its [`Self::GroupId`]. - /// - /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. - async fn add_logical_expression_to_group( - &self, - group_id: Self::GroupId, - logical_expression: Self::LogicalExpression, - children: Vec, - ) -> StorageResult<()>; - - /// Adds a physical expression to an existing group via its [`Self::GroupId`]. - /// - /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. - async fn add_physical_expression_to_group( - &self, - group_id: Self::GroupId, - physical_expression: Self::PhysicalExpression, - children: Vec, - ) -> StorageResult<()>; - - /// Adds a new logical expression into the memo table, creating a new group if the expression - /// does not already exist. - /// - /// The [`Self::LogicalExpression`] type should have some sort of mechanism for checking if - /// the expression has been seen before, and if it has already been created, then the parent - /// group ID should also be retrievable. - /// - /// If the expression already exists, then this function will return the [`Self::GroupId`] of - /// the parent group and the corresponding (already existing) [`Self::LogicalExpressionId`]. - /// - /// If the expression does not exist, this function will create a new group and a new - /// expression, returning brand new IDs for both. - async fn add_logical_expression( - &self, - expression: Self::LogicalExpression, - children: Vec, - ) -> StorageResult<(Self::GroupId, Self::LogicalExpressionId)>; -} diff --git a/optd-persistent/src/memo/mod.rs b/optd-persistent/src/memo/mod.rs deleted file mode 100644 index c455782..0000000 --- a/optd-persistent/src/memo/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod expression; - -pub mod interface; -pub mod orm; diff --git a/optd-persistent/src/memo/orm.rs b/optd-persistent/src/memo/orm.rs deleted file mode 100644 index 6d4ec31..0000000 --- a/optd-persistent/src/memo/orm.rs +++ /dev/null @@ -1,213 +0,0 @@ -use crate::{ - entities::{prelude::*, *}, - BackendManager, {Memo, MemoError, StorageResult}, -}; -use sea_orm::*; - -impl Memo for BackendManager { - type Group = cascades_group::Model; - type GroupId = i32; - type LogicalExpression = logical_expression::Model; - type LogicalExpressionId = i32; - type PhysicalExpression = physical_expression::Model; - type PhysicalExpressionId = i32; - - async fn get_group(&self, group_id: Self::GroupId) -> StorageResult { - Ok(CascadesGroup::find_by_id(group_id) - .one(&self.db) - .await? - .ok_or(MemoError::UnknownGroup)?) - } - - async fn get_all_groups(&self) -> StorageResult> { - Ok(CascadesGroup::find().all(&self.db).await?) - } - - async fn get_logical_expression( - &self, - logical_expression_id: Self::LogicalExpressionId, - ) -> StorageResult { - Ok(LogicalExpression::find_by_id(logical_expression_id) - .one(&self.db) - .await? - .ok_or(MemoError::UnknownLogicalExpression)?) - } - - async fn get_physical_expression( - &self, - physical_expression_id: Self::PhysicalExpressionId, - ) -> StorageResult { - Ok(PhysicalExpression::find_by_id(physical_expression_id) - .one(&self.db) - .await? - .ok_or(MemoError::UnknownPhysicalExpression)?) - } - - async fn get_group_from_logical_expression( - &self, - logical_expression_id: Self::LogicalExpressionId, - ) -> StorageResult { - // Find the logical expression and then look up the field. - Ok(self - .get_logical_expression(logical_expression_id) - .await? - .group_id) - } - - async fn get_group_from_physical_expression( - &self, - physical_expression_id: Self::PhysicalExpressionId, - ) -> StorageResult { - Ok(self - .get_physical_expression(physical_expression_id) - .await? - .group_id) - } - - async fn get_group_logical_expressions( - &self, - group_id: Self::GroupId, - ) -> StorageResult> { - // First retrieve the group record, and then find all related logical expressions. - Ok(self - .get_group(group_id) - .await? - .find_related(LogicalExpression) - .all(&self.db) - .await?) - } - - async fn get_group_physical_expressions( - &self, - group_id: Self::GroupId, - ) -> StorageResult> { - // First retrieve the group record, and then find all related physical expressions. - Ok(self - .get_group(group_id) - .await? - .find_related(PhysicalExpression) - .all(&self.db) - .await?) - } - - async fn get_winner( - &self, - group_id: Self::GroupId, - ) -> StorageResult> { - Ok(self.get_group(group_id).await?.latest_winner) - } - - async fn update_group_winner( - &self, - group_id: Self::GroupId, - physical_expression_id: Self::PhysicalExpressionId, - ) -> StorageResult> { - // First retrieve the group record, and then use an `ActiveModel` to update it. - let mut group = self.get_group(group_id).await?.into_active_model(); - let old_id = group.latest_winner; - - group.latest_winner = Set(Some(physical_expression_id)); - group.update(&self.db).await?; - - // The old value must be set (`None` still means it has been set). - let old = old_id.unwrap(); - Ok(old) - } - - async fn add_logical_expression_to_group( - &self, - group_id: Self::GroupId, - logical_expression: Self::LogicalExpression, - _children: Vec, - ) -> StorageResult<()> { - if logical_expression.group_id != group_id { - Err(MemoError::InvalidExpression)? - } - - // Check if the group actually exists. - let _ = self.get_group(group_id).await?; - - // Insert the expression. - let _ = logical_expression - .into_active_model() - .insert(&self.db) - .await?; - - todo!("add the children of the logical expression into the children table") - } - - async fn add_physical_expression_to_group( - &self, - group_id: Self::GroupId, - physical_expression: Self::PhysicalExpression, - _children: Vec, - ) -> StorageResult<()> { - if physical_expression.group_id != group_id { - Err(MemoError::InvalidExpression)? - } - - // Check if the group actually exists. - let _ = self.get_group(group_id).await?; - - // Insert the expression. - let _ = physical_expression - .into_active_model() - .insert(&self.db) - .await?; - - todo!("add the children of the logical expression into the children table") - } - - /// Note that in this function, we ignore the group ID that the logical expression contains. - async fn add_logical_expression( - &self, - expression: Self::LogicalExpression, - _children: Vec, - ) -> StorageResult<(Self::GroupId, Self::LogicalExpressionId)> { - // Lookup all expressions that have the same fingerprint. There may be false positives, but - // we will check for those later. - let fingerprint = expression.fingerprint; - let potential_matches = LogicalExpression::find() - .filter(logical_expression::Column::Fingerprint.eq(fingerprint)) - .all(&self.db) - .await?; - - // Of the expressions that have the same fingerprint, check if there already exists an - // expression that is exactly identical to the input expression. - let mut matches: Vec<_> = potential_matches - .into_iter() - .filter(|expr| expr == &expression) - .collect(); - assert!( - matches.len() <= 1, - "there cannot be more than 1 exact logical expression match" - ); - - // The expression already exists, so return its data. - if !matches.is_empty() { - let existing_expression = matches - .pop() - .expect("we just checked that an element exists"); - - return Ok((existing_expression.group_id, existing_expression.id)); - } - - // The expression does not exist yet, so we need to create a new group and new expression. - let group = cascades_group::ActiveModel { - latest_winner: Set(None), - in_progress: Set(false), - is_optimized: Set(false), - ..Default::default() - }; - - // Insert a new group. - let res = cascades_group::Entity::insert(group).exec(&self.db).await?; - - // Insert the input expression with the correct `group_id`. - let mut new_expr = expression.into_active_model(); - new_expr.group_id = Set(res.last_insert_id); - let new_expr = new_expr.insert(&self.db).await?; - - Ok((new_expr.group_id, new_expr.id)) - } -} diff --git a/optd-persistent/src/migrator/catalog/m20241029_000001_attribute.rs b/optd-persistent/src/migrator/catalog/m20241029_000001_attribute.rs deleted file mode 100644 index 543c745..0000000 --- a/optd-persistent/src/migrator/catalog/m20241029_000001_attribute.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::migrator::catalog::table_metadata::TableMetadata; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(Iden)] -pub enum Attribute { - Table, - Id, - TableId, - Name, - CompressionMethod, - VariantTag, - BaseAttributeNumber, - IsNotNull, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(Attribute::Table) - .if_not_exists() - .col(pk_auto(Attribute::Id)) - .col(integer(Attribute::TableId)) - .foreign_key( - ForeignKey::create() - .from(Attribute::Table, Attribute::TableId) - .to(TableMetadata::Table, TableMetadata::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(string(Attribute::Name)) - .col(char(Attribute::CompressionMethod)) - .col(integer(Attribute::VariantTag)) - .col(integer(Attribute::BaseAttributeNumber)) - .col(boolean(Attribute::IsNotNull)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(Attribute::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/catalog/m20241029_000001_attribute_constraint_junction.rs b/optd-persistent/src/migrator/catalog/m20241029_000001_attribute_constraint_junction.rs deleted file mode 100644 index c97ff99..0000000 --- a/optd-persistent/src/migrator/catalog/m20241029_000001_attribute_constraint_junction.rs +++ /dev/null @@ -1,69 +0,0 @@ -//! An entity representing the relationship between [`attribute`] and [`constraint`]. -//! -//! If a constraint is a table constraint (including foreign keys, but not constraint triggers), -//! the attributes that are constrained on are stored in the [`attribute_constraint_junction`]. -//! -//! One constraint might be associated with multiple attributes, for example, a composite primary key. - -use crate::migrator::catalog::{attribute::Attribute, constraint_metadata::ConstraintMetadata}; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(Iden)] -pub enum AttributeConstraintJunction { - Table, - AttributeId, - ConstraintId, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(AttributeConstraintJunction::Table) - .if_not_exists() - .col(integer(AttributeConstraintJunction::AttributeId)) - .col(integer(AttributeConstraintJunction::ConstraintId)) - .primary_key( - Index::create() - .col(AttributeConstraintJunction::AttributeId) - .col(AttributeConstraintJunction::ConstraintId), - ) - .foreign_key( - ForeignKey::create() - .from( - AttributeConstraintJunction::Table, - AttributeConstraintJunction::AttributeId, - ) - .to(Attribute::Table, Attribute::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .foreign_key( - ForeignKey::create() - .from( - AttributeConstraintJunction::Table, - AttributeConstraintJunction::ConstraintId, - ) - .to(ConstraintMetadata::Table, ConstraintMetadata::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .to_owned(), - ) - .await - } - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table( - Table::drop() - .table(AttributeConstraintJunction::Table) - .to_owned(), - ) - .await - } -} diff --git a/optd-persistent/src/migrator/catalog/m20241029_000001_attribute_foreign_constraint_junction.rs b/optd-persistent/src/migrator/catalog/m20241029_000001_attribute_foreign_constraint_junction.rs deleted file mode 100644 index 7b95600..0000000 --- a/optd-persistent/src/migrator/catalog/m20241029_000001_attribute_foreign_constraint_junction.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! An entity representing the relationship between [`attribute`] and [`constraint`]. -//! -//! If a constraint is a foreign key constraint, the attributes that are referenced by the foreign -//! key are stored in the [`attribute_foreign_constraint_junction`]. Note that this is different from -//! the [`attribute_constraint_junction`] table, which stores the attributes that are constrained on. -//! In the case of a foreign key constraint, this refers to the attributes that are referecing from the -//! foreign key. -//! -//! One foreign key constraint might be associated with multiple attributes, for example, a composite -//! foreign key. - -use crate::migrator::catalog::{attribute::Attribute, constraint_metadata::ConstraintMetadata}; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(Iden)] -pub enum AttributeForeignConstraintJunction { - Table, - AttributeId, - ConstraintId, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(AttributeForeignConstraintJunction::Table) - .if_not_exists() - .col(integer(AttributeForeignConstraintJunction::AttributeId)) - .col(integer(AttributeForeignConstraintJunction::ConstraintId)) - .primary_key( - Index::create() - .col(AttributeForeignConstraintJunction::AttributeId) - .col(AttributeForeignConstraintJunction::ConstraintId), - ) - .foreign_key( - ForeignKey::create() - .from( - AttributeForeignConstraintJunction::Table, - AttributeForeignConstraintJunction::AttributeId, - ) - .to(Attribute::Table, Attribute::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .foreign_key( - ForeignKey::create() - .from( - AttributeForeignConstraintJunction::Table, - AttributeForeignConstraintJunction::ConstraintId, - ) - .to(ConstraintMetadata::Table, ConstraintMetadata::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .to_owned(), - ) - .await - } - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table( - Table::drop() - .table(AttributeForeignConstraintJunction::Table) - .to_owned(), - ) - .await - } -} diff --git a/optd-persistent/src/migrator/catalog/m20241029_000001_constraint_metadata.rs b/optd-persistent/src/migrator/catalog/m20241029_000001_constraint_metadata.rs deleted file mode 100644 index f6435eb..0000000 --- a/optd-persistent/src/migrator/catalog/m20241029_000001_constraint_metadata.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::migrator::catalog::{index_metadata::IndexMetadata, table_metadata::TableMetadata}; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(Iden)] -pub enum ConstraintMetadata { - Table, - Id, - Name, - VariantTag, - TableId, - IndexId, - ForeignRefId, - CheckSrc, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(ConstraintMetadata::Table) - .if_not_exists() - .col(pk_auto(ConstraintMetadata::Id)) - .col(string(ConstraintMetadata::Name)) - .col(integer(ConstraintMetadata::VariantTag)) - .col(integer_null(ConstraintMetadata::TableId)) - .foreign_key( - ForeignKey::create() - .from(ConstraintMetadata::Table, ConstraintMetadata::TableId) - .to(TableMetadata::Table, TableMetadata::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(integer_null(ConstraintMetadata::IndexId)) - .foreign_key( - ForeignKey::create() - .from(ConstraintMetadata::Table, ConstraintMetadata::IndexId) - .to(IndexMetadata::Table, IndexMetadata::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(integer_null(ConstraintMetadata::ForeignRefId)) - .foreign_key( - ForeignKey::create() - .from(ConstraintMetadata::Table, ConstraintMetadata::ForeignRefId) - .to(TableMetadata::Table, TableMetadata::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(string(ConstraintMetadata::CheckSrc)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(ConstraintMetadata::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/catalog/m20241029_000001_database_metadata.rs b/optd-persistent/src/migrator/catalog/m20241029_000001_database_metadata.rs deleted file mode 100644 index 4423557..0000000 --- a/optd-persistent/src/migrator/catalog/m20241029_000001_database_metadata.rs +++ /dev/null @@ -1,35 +0,0 @@ -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(Iden)] -pub enum DatabaseMetadata { - Table, - Id, - Name, - CreationTime, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(DatabaseMetadata::Table) - .if_not_exists() - .col(pk_auto(DatabaseMetadata::Id)) - .col(string(DatabaseMetadata::Name)) - .col(timestamp(DatabaseMetadata::CreationTime)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(DatabaseMetadata::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/catalog/m20241029_000001_index_metadata.rs b/optd-persistent/src/migrator/catalog/m20241029_000001_index_metadata.rs deleted file mode 100644 index 1d51a6f..0000000 --- a/optd-persistent/src/migrator/catalog/m20241029_000001_index_metadata.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::migrator::catalog::table_metadata::TableMetadata; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(Iden)] -pub enum IndexMetadata { - Table, - Id, - TableId, - Name, - NumberOfAttributes, - VariantTag, - IsUnique, - NullsNotDistinct, - IsPrimary, - IsClustered, - IsExclusion, - Description, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(IndexMetadata::Table) - .if_not_exists() - .col(pk_auto(IndexMetadata::Id)) - .col(integer(IndexMetadata::TableId)) - .foreign_key( - ForeignKey::create() - .from(IndexMetadata::Table, IndexMetadata::TableId) - .to(TableMetadata::Table, TableMetadata::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(string(IndexMetadata::Name)) - .col(integer(IndexMetadata::NumberOfAttributes)) - .col(integer(IndexMetadata::VariantTag)) - .col(boolean(IndexMetadata::IsUnique)) - .col(boolean(IndexMetadata::NullsNotDistinct)) - .col(boolean(IndexMetadata::IsPrimary)) - .col(boolean(IndexMetadata::IsClustered)) - .col(boolean(IndexMetadata::IsExclusion)) - .col(string(IndexMetadata::Description)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(IndexMetadata::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/catalog/m20241029_000001_namespace_metadata.rs b/optd-persistent/src/migrator/catalog/m20241029_000001_namespace_metadata.rs deleted file mode 100644 index 684621c..0000000 --- a/optd-persistent/src/migrator/catalog/m20241029_000001_namespace_metadata.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::migrator::catalog::database_metadata::DatabaseMetadata; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(Iden)] -pub enum NamespaceMetadata { - Table, - Id, - Name, - DatabaseId, - CreationTime, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(NamespaceMetadata::Table) - .if_not_exists() - .col(pk_auto(NamespaceMetadata::Id)) - .col(string(NamespaceMetadata::Name)) - .col(integer(NamespaceMetadata::DatabaseId)) - .foreign_key( - ForeignKey::create() - .from(NamespaceMetadata::Table, NamespaceMetadata::DatabaseId) - .to(DatabaseMetadata::Table, DatabaseMetadata::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(timestamp(NamespaceMetadata::CreationTime)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(NamespaceMetadata::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/catalog/m20241029_000001_table_metadata.rs b/optd-persistent/src/migrator/catalog/m20241029_000001_table_metadata.rs deleted file mode 100644 index 66a0ec3..0000000 --- a/optd-persistent/src/migrator/catalog/m20241029_000001_table_metadata.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::migrator::catalog::namespace_metadata::NamespaceMetadata; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(Iden)] -pub enum TableMetadata { - Table, - Id, - Name, - NamespaceId, - CreationTime, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(TableMetadata::Table) - .if_not_exists() - .col(pk_auto(TableMetadata::Id)) - .col(string(TableMetadata::Name)) - .col(integer(TableMetadata::NamespaceId)) - .foreign_key( - ForeignKey::create() - .from(TableMetadata::Table, TableMetadata::NamespaceId) - .to(NamespaceMetadata::Table, NamespaceMetadata::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(timestamp(TableMetadata::CreationTime)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(TableMetadata::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/catalog/m20241029_000001_trigger.rs b/optd-persistent/src/migrator/catalog/m20241029_000001_trigger.rs deleted file mode 100644 index c2bf0eb..0000000 --- a/optd-persistent/src/migrator/catalog/m20241029_000001_trigger.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::migrator::catalog::table_metadata::TableMetadata; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(Iden)] -pub enum Trigger { - Table, - Id, - Name, - TableId, - ParentTriggerId, - Function, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(Trigger::Table) - .if_not_exists() - .col(pk_auto(Trigger::Id)) - .col(string(Trigger::Name)) - .col(integer(Trigger::TableId)) - .foreign_key( - ForeignKey::create() - .from(Trigger::Table, Trigger::TableId) - .to(TableMetadata::Table, TableMetadata::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(integer(Trigger::ParentTriggerId)) - .foreign_key( - ForeignKey::create() - .from(Trigger::Table, Trigger::ParentTriggerId) - .to(Trigger::Table, Trigger::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(json(Trigger::Function)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(Trigger::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/catalog/mod.rs b/optd-persistent/src/migrator/catalog/mod.rs deleted file mode 100644 index bef234a..0000000 --- a/optd-persistent/src/migrator/catalog/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -pub(crate) mod m20241029_000001_attribute; -pub(crate) mod m20241029_000001_attribute_constraint_junction; -pub(crate) mod m20241029_000001_attribute_foreign_constraint_junction; -pub(crate) mod m20241029_000001_constraint_metadata; -pub(crate) mod m20241029_000001_database_metadata; -pub(crate) mod m20241029_000001_index_metadata; -pub(crate) mod m20241029_000001_namespace_metadata; -pub(crate) mod m20241029_000001_table_metadata; -pub(crate) mod m20241029_000001_trigger; - -pub(crate) use m20241029_000001_attribute as attribute; -pub(crate) use m20241029_000001_attribute_constraint_junction as attribute_constraint_junction; -pub(crate) use m20241029_000001_attribute_foreign_constraint_junction as attribute_foreign_constraint_junction; -pub(crate) use m20241029_000001_constraint_metadata as constraint_metadata; -pub(crate) use m20241029_000001_database_metadata as database_metadata; -pub(crate) use m20241029_000001_index_metadata as index_metadata; -pub(crate) use m20241029_000001_namespace_metadata as namespace_metadata; -pub(crate) use m20241029_000001_table_metadata as table_metadata; -pub(crate) use m20241029_000001_trigger as trigger; diff --git a/optd-persistent/src/migrator/cost_model/m20241029_000001_event.rs b/optd-persistent/src/migrator/cost_model/m20241029_000001_event.rs deleted file mode 100644 index 9fe326f..0000000 --- a/optd-persistent/src/migrator/cost_model/m20241029_000001_event.rs +++ /dev/null @@ -1,40 +0,0 @@ -//! Every time we insert/update statistics, we need to insert a new -//! row into this table to record the event. - -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(Iden)] -pub enum Event { - Table, - EpochId, - Timestamp, - SourceVariant, - Data, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(Event::Table) - .if_not_exists() - .col(pk_auto(Event::EpochId)) - .col(timestamp(Event::Timestamp)) - .col(string(Event::SourceVariant)) - .col(json(Event::Data)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(Event::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/cost_model/m20241029_000001_physical_expression_to_statistic_junction.rs b/optd-persistent/src/migrator/cost_model/m20241029_000001_physical_expression_to_statistic_junction.rs deleted file mode 100644 index 06b8ac4..0000000 --- a/optd-persistent/src/migrator/cost_model/m20241029_000001_physical_expression_to_statistic_junction.rs +++ /dev/null @@ -1,75 +0,0 @@ -//! This table stores for a physical expression, which statistics are used, so we -//! don't need to compute it again. It is especially useful for update_stats, where -//! we need to invalidate all the costs based on the physical_expression_id, so we -//! need to use this table to get the physical_expression_id via statistic_id. -//! -//! **NOTE:** When we compute the cost for a physical expression, we should also -//! insert related mappings into this table. - -use crate::migrator::cost_model::statistic::Statistic; -use crate::migrator::memo::physical_expression::PhysicalExpression; - -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(Iden)] -pub enum PhysicalExpressionToStatisticJunction { - Table, - PhysicalExpressionId, - StatisticId, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(PhysicalExpressionToStatisticJunction::Table) - .if_not_exists() - .col(integer( - PhysicalExpressionToStatisticJunction::PhysicalExpressionId, - )) - .col(integer(PhysicalExpressionToStatisticJunction::StatisticId)) - .primary_key( - Index::create() - .col(PhysicalExpressionToStatisticJunction::PhysicalExpressionId) - .col(PhysicalExpressionToStatisticJunction::StatisticId), - ) - .foreign_key( - ForeignKey::create() - .from( - PhysicalExpressionToStatisticJunction::Table, - PhysicalExpressionToStatisticJunction::PhysicalExpressionId, - ) - .to(PhysicalExpression::Table, PhysicalExpression::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .foreign_key( - ForeignKey::create() - .from( - PhysicalExpressionToStatisticJunction::Table, - PhysicalExpressionToStatisticJunction::StatisticId, - ) - .to(Statistic::Table, Statistic::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table( - Table::drop() - .table(PhysicalExpressionToStatisticJunction::Table) - .to_owned(), - ) - .await - } -} diff --git a/optd-persistent/src/migrator/cost_model/m20241029_000001_plan_cost.rs b/optd-persistent/src/migrator/cost_model/m20241029_000001_plan_cost.rs deleted file mode 100644 index fdd2fef..0000000 --- a/optd-persistent/src/migrator/cost_model/m20241029_000001_plan_cost.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! When a statistic is updated, then all the related costs should be invalidated. (IsValid is set to false) -//! This design (using IsValid flag) is based on the assumption that update_stats will not be called very frequently. -//! It favors the compute_cost performance over the update_stats performance. -//! -//! This file stores cost like compute_cost, io_cost, network_cost, etc. for each physical expression. It also -//! stores the estimated output row count (estimated statistic) of each physical expression. -//! Sometimes we only have one of them to store, so we make Cost and EstimatedStatistic optional. But -//! one record must have at least one of them. -//! -//! TODO: Ideally, we can separate them since sometimes we only have the estimated output row count to store, -//! (when calling `derive_statistic`) but we don't have the detailed cost. - -use crate::migrator::cost_model::event::Event; -use crate::migrator::memo::physical_expression::PhysicalExpression; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(Iden)] -pub enum PlanCost { - Table, - Id, - PhysicalExpressionId, - EpochId, - // It is json type, including computation cost, I/O cost, etc. - Cost, - // Raw estimated output row count of this expression - EstimatedStatistic, - // Whether the cost is valid or not. If the latest cost for an expr is invalid, then we need to recompute the cost. - // We need to invalidate the cost when the related stats are updated. - IsValid, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(PlanCost::Table) - .if_not_exists() - .col(pk_auto(PlanCost::Id)) - .col(integer(PlanCost::PhysicalExpressionId)) - .foreign_key( - ForeignKey::create() - .from(PlanCost::Table, PlanCost::PhysicalExpressionId) - .to(PhysicalExpression::Table, PhysicalExpression::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(integer(PlanCost::EpochId)) - .foreign_key( - ForeignKey::create() - .from(PlanCost::Table, PlanCost::EpochId) - .to(Event::Table, Event::EpochId) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(json_null(PlanCost::Cost)) - .col(float_null(PlanCost::EstimatedStatistic)) - .col(boolean(PlanCost::IsValid)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(PlanCost::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/cost_model/m20241029_000001_statistic.rs b/optd-persistent/src/migrator/cost_model/m20241029_000001_statistic.rs deleted file mode 100644 index 3b2bb3c..0000000 --- a/optd-persistent/src/migrator/cost_model/m20241029_000001_statistic.rs +++ /dev/null @@ -1,64 +0,0 @@ -//! This table stores the statistic infos. One sole statistic only has one row in this table. -//! -//! If we want to insert a new statistic, we should first insert one row into this table, then add a new -//! event, and finally insert the statistic value into the versioned_statistic table. -//! If we want to update a statistic, we should first find the real statistic id from this table, then -//! add a new event, and finally insert the statistic value into the versioned_statistic table. - -use crate::migrator::catalog::m20241029_000001_table_metadata::TableMetadata; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(Iden)] -pub enum Statistic { - Table, - Id, - Name, - // null if not a table statistic. - TableId, - CreationTime, - // 0 if a table statistic. - NumberOfAttributes, - VariantTag, - // Store the sorted attribute ids of this statistic, to support quick lookup (OR we can use junction table to look up) - // For example, if we want to store the statistic of attributes [1, 2, 3], we can store it as "1,2,3". - // During lookup, we should first sort the attribute ids, and then look up. - // OR we can use statistic_to_attribute_junction table to look up. - Description, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(Statistic::Table) - .if_not_exists() - .col(pk_auto(Statistic::Id)) - .col(string(Statistic::Name)) - .col(integer_null(Statistic::TableId)) - .foreign_key( - ForeignKey::create() - .from(Statistic::Table, Statistic::TableId) - .to(TableMetadata::Table, TableMetadata::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(timestamp(Statistic::CreationTime)) - .col(integer(Statistic::NumberOfAttributes)) - .col(integer(Statistic::VariantTag)) - .col(string(Statistic::Description)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(Statistic::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/cost_model/m20241029_000001_statistic_to_attribute_junction.rs b/optd-persistent/src/migrator/cost_model/m20241029_000001_statistic_to_attribute_junction.rs deleted file mode 100644 index 739675b..0000000 --- a/optd-persistent/src/migrator/cost_model/m20241029_000001_statistic_to_attribute_junction.rs +++ /dev/null @@ -1,70 +0,0 @@ -//! An entity representing the relationship between [`statistic`] and [`attribute`]. -//! -//! One [`statistic`] can be associated with multiple [`attribute`]s, which denotes a joint -//! statistic for the attributes. On the other hand, one [`attribute`] can be associated with -//! multiple [`statistic`]s, since the attribute can be used in multiple statistics. - -use crate::migrator::catalog::attribute::Attribute; -use crate::migrator::cost_model::statistic::Statistic; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(Iden)] -pub enum StatisticToAttributeJunction { - Table, - StatisticId, - AttributeId, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(StatisticToAttributeJunction::Table) - .if_not_exists() - .col(integer(StatisticToAttributeJunction::StatisticId)) - .col(integer(StatisticToAttributeJunction::AttributeId)) - .primary_key( - Index::create() - .col(StatisticToAttributeJunction::StatisticId) - .col(StatisticToAttributeJunction::AttributeId), - ) - .foreign_key( - ForeignKey::create() - .from( - StatisticToAttributeJunction::Table, - StatisticToAttributeJunction::StatisticId, - ) - .to(Statistic::Table, Statistic::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .foreign_key( - ForeignKey::create() - .from( - StatisticToAttributeJunction::Table, - StatisticToAttributeJunction::AttributeId, - ) - .to(Attribute::Table, Attribute::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table( - Table::drop() - .table(StatisticToAttributeJunction::Table) - .to_owned(), - ) - .await - } -} diff --git a/optd-persistent/src/migrator/cost_model/m20241029_000001_versioned_statistic.rs b/optd-persistent/src/migrator/cost_model/m20241029_000001_versioned_statistic.rs deleted file mode 100644 index f31d011..0000000 --- a/optd-persistent/src/migrator/cost_model/m20241029_000001_versioned_statistic.rs +++ /dev/null @@ -1,57 +0,0 @@ -//! This table stores the versioned statistics. It includes all the histories of the statistics. -//! -//! If a statistic is updated/inserted, please insert one new row into this table. - -use crate::migrator::cost_model::{event::Event, statistic::Statistic}; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(Iden)] -pub enum VersionedStatistic { - Table, - Id, - EpochId, - // Real statistic id. - StatisticId, - StatisticValue, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(VersionedStatistic::Table) - .if_not_exists() - .col(pk_auto(VersionedStatistic::Id)) - .col(integer(VersionedStatistic::EpochId)) - .foreign_key( - ForeignKey::create() - .from(VersionedStatistic::Table, VersionedStatistic::EpochId) - .to(Event::Table, Event::EpochId) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(integer(VersionedStatistic::StatisticId)) - .foreign_key( - ForeignKey::create() - .from(VersionedStatistic::Table, VersionedStatistic::StatisticId) - .to(Statistic::Table, Statistic::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(json(VersionedStatistic::StatisticValue)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(VersionedStatistic::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/cost_model/mod.rs b/optd-persistent/src/migrator/cost_model/mod.rs deleted file mode 100644 index 5f8dad1..0000000 --- a/optd-persistent/src/migrator/cost_model/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub(crate) mod m20241029_000001_event; -pub(crate) mod m20241029_000001_physical_expression_to_statistic_junction; -pub(crate) mod m20241029_000001_plan_cost; -pub(crate) mod m20241029_000001_statistic; -pub(crate) mod m20241029_000001_statistic_to_attribute_junction; -pub(crate) mod m20241029_000001_versioned_statistic; - -pub(crate) use m20241029_000001_event as event; -pub(crate) use m20241029_000001_physical_expression_to_statistic_junction as physical_expression_to_statistic_junction; -pub(crate) use m20241029_000001_plan_cost as plan_cost; -pub(crate) use m20241029_000001_statistic as statistic; -pub(crate) use m20241029_000001_statistic_to_attribute_junction as statistic_to_attribute_junction; -pub(crate) use m20241029_000001_versioned_statistic as versioned_statistic; diff --git a/optd-persistent/src/migrator/memo/m20241029_000001_cascades_group.rs b/optd-persistent/src/migrator/memo/m20241029_000001_cascades_group.rs deleted file mode 100644 index 1700772..0000000 --- a/optd-persistent/src/migrator/memo/m20241029_000001_cascades_group.rs +++ /dev/null @@ -1,121 +0,0 @@ -//! An entity representing a group / equivalence class in the Cascades framework. -//! -//! Quoted from the Microsoft article _Extensible query optimizers in practice_: -//! -//! > In the memo, each class of equivalent expressions is called an equivalent class or a group, -//! > and all equivalent expressions within the class are called group expressions or simply -//! > expressions. -//! -//! A Cascades group is defined as a class of equivalent logical or physical expressions. The -//! Cascades framework uses these groups as a way of storing the best query sub-plans for use in the -//! dynamic programming search algorithm. -//! -//! For example, a Cascades group could be the set of expressions containing the logical expressions -//! `Join(A, B)` and `Join(B, A)`, as well as the physical expressions `HashJoin(A, B)` and -//! `NestedLoopJoin(B, A)`. -//! -//! # Columns -//! -//! Each group is assigned a monotonically-increasing (unique) ID. This ID will be important since -//! there are many foreign key references from other tables to `cascades_group`. -//! -//! We additionally store a `latest_winner` foreign key reference to a physical expression. See -//! the [section](#best-physical-plan-winner) below for more details. -//! -//! Finally, we store `in_progress` and `is_optimized` flags that are used for quickly determining -//! the state of optimization for this group during the dynamic programming search. -//! -//! # Entity Relationships -//! -//! ### Child Expressions (Logical and Physical) -//! -//! To retrieve all of a `cascades_group`'s equivalent expressions, you must query the -//! [`logical_expression`] or the [`physical_expression`] entities via their foreign keys to -//! `cascades_group`. The relationship between [`logical_expression`] and `cascades_group` is -//! many-to-one, and the exact same many-to-one relationship is held for [`physical_expression`] to -//! `cascades_group`. -//! -//! ### Parent Expressions (Logical and Physical) -//! -//! Additionally, each logical or physical expression can have any number of `cascades_group`s as -//! children, and a group can be a child of any expression. Thus, `cascades_group` additionally has -//! a many-to-many relationship with [`logical_expression`] and [`physical_expression`] via the -//! [`logical_children`] and [`physical_children`] entities. -//! -//! To reiterate, `cascades_group` has **both** a one-to-many **and** a many-to-many relationship -//! with both [`logical_expression`] and [`physical_expression`]. This is due to groups being both -//! parents and children of expressions. -//! -//! ### Best Physical Plan (Winner) -//! -//! The `cascades_group` entity also stores a `latest_winner` _nullable_ foreign key reference to -//! a physical expression. This represents the most recent best query plan we have computed. The -//! reason it is nullable is because we may not have come up with any best query plan yet. -//! -//! ### Logical Properties -//! -//! Lastly, each `cascades_group` record will have a set of logical properties store in the -//! [`logical_property`] entity, where there is an many-to-one relationship from -//! [`logical_property`] to `cascades_group`. Note that we do not store physical properties directly -//! on the `cascades_group`, but rather we store them for each [`physical_expression`] record. -//! -//! [`logical_expression`]: super::logical_expression -//! [`physical_expression`]: super::physical_expression -//! [`logical_children`]: super::logical_children -//! [`physical_children`]: super::physical_children -//! [`logical_property`]: super::logical_property - -use crate::migrator::memo::physical_expression::PhysicalExpression; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(DeriveIden)] -pub enum CascadesGroup { - Table, - Id, - LatestWinner, - InProgress, - IsOptimized, - ParentId, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(CascadesGroup::Table) - .if_not_exists() - .col(pk_auto(CascadesGroup::Id)) - .col(integer_null(CascadesGroup::LatestWinner)) - .foreign_key( - ForeignKey::create() - .from(CascadesGroup::Table, CascadesGroup::LatestWinner) - .to(PhysicalExpression::Table, PhysicalExpression::Id) - .on_delete(ForeignKeyAction::SetNull) - .on_update(ForeignKeyAction::Cascade), - ) - .col(boolean(CascadesGroup::InProgress)) - .col(boolean(CascadesGroup::IsOptimized)) - .col(integer_null(CascadesGroup::ParentId)) - .foreign_key( - ForeignKey::create() - .from(CascadesGroup::Table, CascadesGroup::ParentId) - .to(CascadesGroup::Table, CascadesGroup::Id) - .on_delete(ForeignKeyAction::SetNull) - .on_update(ForeignKeyAction::Cascade), - ) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(CascadesGroup::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/memo/m20241029_000001_group_winner.rs b/optd-persistent/src/migrator/memo/m20241029_000001_group_winner.rs deleted file mode 100644 index bd952c9..0000000 --- a/optd-persistent/src/migrator/memo/m20241029_000001_group_winner.rs +++ /dev/null @@ -1,103 +0,0 @@ -//! An entity representing a the best physical plan (or "winner") of a Cascades group. -//! -//! In the Cascades framework, query optimization is done through dynamic programming that is based -//! on the assumption that the cost model satisfies the _principle of optimality_. Quoted from the -//! Microsoft article _Extensible query optimizers in practice_: -//! -//! > ... in the search space of linear sequence of joins, the optimal plan for a join of n -//! > relations can be found by extending the optimal plan of a sub-expression of n - 1 joins with -//! > an additional join. -//! -//! By storing the best sub-plans / [`physical_expression`]s of smaller Cascades groups, we can -//! build up an optimal query plan. -//! -//! This entity represents the best plan sub-tree for a specific group. However, we store multiple -//! winners over different epochs, as changes to the database may require us to re-evaluate what the -//! optimal sub-plan is. -//! -//! # Columns -//! -//! Other than the primary key, all of the columns in this relation are foreign keys to other -//! tables. -//! -//! A group winner is defined by the [`cascades_group`] it belongs to (`group_id`), the unique ID of -//! the [`physical_expression`] (`physical_expression_id`), the ID of the cost record in the -//! [`plan_cost`] table (`cost_id`), and the monotonically-increasing epoch ID in the [`event`] -//! table (`epoch_id`). -//! -//! [`cascades_group`]: super::cascades_group -//! [`physical_expression`]: super::physical_expression -//! [`plan_cost`]: super::super::cost_model::plan_cost -//! [`event`]: super::super::cost_model::event - -use crate::migrator::cost_model::{event::Event, plan_cost::PlanCost}; -use crate::migrator::memo::{ - cascades_group::CascadesGroup, physical_expression::PhysicalExpression, -}; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(Iden)] -pub enum GroupWinner { - Table, - Id, - GroupId, - PhysicalExpressionId, - CostId, - EpochId, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(GroupWinner::Table) - .if_not_exists() - .col(pk_auto(GroupWinner::Id)) - .col(integer(GroupWinner::GroupId)) - .foreign_key( - ForeignKey::create() - .from(GroupWinner::Table, GroupWinner::GroupId) - .to(CascadesGroup::Table, CascadesGroup::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(integer(GroupWinner::PhysicalExpressionId)) - .foreign_key( - ForeignKey::create() - .from(GroupWinner::Table, GroupWinner::PhysicalExpressionId) - .to(PhysicalExpression::Table, PhysicalExpression::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(integer(GroupWinner::CostId)) - .foreign_key( - ForeignKey::create() - .from(GroupWinner::Table, GroupWinner::CostId) - .to(PlanCost::Table, PlanCost::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(integer(GroupWinner::EpochId)) - .foreign_key( - ForeignKey::create() - .from(GroupWinner::Table, GroupWinner::EpochId) - .to(Event::Table, Event::EpochId) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(GroupWinner::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/memo/m20241029_000001_logical_children.rs b/optd-persistent/src/migrator/memo/m20241029_000001_logical_children.rs deleted file mode 100644 index d0835f4..0000000 --- a/optd-persistent/src/migrator/memo/m20241029_000001_logical_children.rs +++ /dev/null @@ -1,65 +0,0 @@ -//! An entity representing the [`cascades_group`] children of every [`logical_expression`]. -//! -//! Formally, this entity is a junction which allows us to represent a many-to-many relationship -//! between [`logical_expression`] and [`cascades_group`]. Expressions can have any number of child -//! groups, and every group can be a child of many different expressions, hence the many-to-many -//! relationship. -//! -//! See [`cascades_group`] for more details. -//! -//! [`cascades_group`]: super::cascades_group -//! [`logical_expression`]: super::logical_expression - -use crate::migrator::memo::{cascades_group::CascadesGroup, logical_expression::LogicalExpression}; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(DeriveIden)] -pub enum LogicalChildren { - Table, - LogicalExpressionId, - GroupId, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(LogicalChildren::Table) - .if_not_exists() - .col(integer(LogicalChildren::LogicalExpressionId)) - .col(integer(LogicalChildren::GroupId)) - .primary_key( - Index::create() - .col(LogicalChildren::LogicalExpressionId) - .col(LogicalChildren::GroupId), - ) - .foreign_key( - ForeignKey::create() - .from(LogicalChildren::Table, LogicalChildren::GroupId) - .to(LogicalExpression::Table, LogicalExpression::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .foreign_key( - ForeignKey::create() - .from(LogicalChildren::Table, LogicalChildren::GroupId) - .to(CascadesGroup::Table, CascadesGroup::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(LogicalChildren::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/memo/m20241029_000001_logical_expression.rs b/optd-persistent/src/migrator/memo/m20241029_000001_logical_expression.rs deleted file mode 100644 index 71e28d3..0000000 --- a/optd-persistent/src/migrator/memo/m20241029_000001_logical_expression.rs +++ /dev/null @@ -1,82 +0,0 @@ -//! An entity representing a logical plan expression in the Cascades framework. -//! -//! Quoted from the Microsoft article _Extensible query optimizers in practice_: -//! -//! > A logical expression is defined as a tree of logical operators, and corresponds to a -//! > relational algebraic expression. -//! -//! In the Cascades query optimization framework, the memo table stores equivalence classes of -//! expressions (see [`cascades_group`]). These equivalence classes, or "groups", store both -//! `logical_expression`s and [`physical_expression`]s. -//! -//! Optimization starts by "exploring" equivalent logical expressions within a group. For example, -//! the logical expressions `Join(A, B)` and `Join(B, A)` are contained in the same group. The -//! logical expressions are defined as a `Join` operator with the groups representing a scan of -//! table `A` and a scan of table `B` as its children. -//! -//! # Columns -//! -//! Each `logical_expression` has a unique primary key ID, but it holds little importance other than -//! helping distinguish between two different expressions. -//! -//! The more interesting column is the `fingerprint` column, in which we store a hashed fingerprint -//! value that can be used to efficiently check equality between two potentially equivalent logical -//! expressions (hash-consing). See ???TODO??? for more information on expression fingerprints. -//! -//! Finally, since there are many different types of operators, we store a variant tag and a data -//! column as JSON to represent the semi-structured data fields of logical operators. -//! -//! # Entity Relationships -//! -//! The only relationship that `logical_expression` has is to [`cascades_group`]. It has **both** a -//! one-to-many **and** a many-to-many relationship with [`cascades_group`], and you can see more -//! details about this in the module-level documentation for [`cascades_group`]. -//! -//! [`cascades_group`]: super::cascades_group -//! [`physical_expression`]: super::physical_expression - -use crate::migrator::memo::cascades_group::CascadesGroup; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(DeriveIden)] -pub enum LogicalExpression { - Table, - Id, - GroupId, - Fingerprint, - VariantTag, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(LogicalExpression::Table) - .if_not_exists() - .col(pk_auto(LogicalExpression::Id)) - .col(integer(LogicalExpression::GroupId)) - .foreign_key( - ForeignKey::create() - .from(LogicalExpression::Table, LogicalExpression::GroupId) - .to(CascadesGroup::Table, CascadesGroup::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(big_unsigned(LogicalExpression::Fingerprint)) - .col(small_integer(LogicalExpression::VariantTag)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(LogicalExpression::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/memo/m20241029_000001_logical_property.rs b/optd-persistent/src/migrator/memo/m20241029_000001_logical_property.rs deleted file mode 100644 index 4b10da6..0000000 --- a/optd-persistent/src/migrator/memo/m20241029_000001_logical_property.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! An entity representing a logical property of a Cascades group. -//! -//! TODO what exactly are we storing in here? -//! TODO why is it linked to only cascades groups and not logical expressions? - -use crate::migrator::memo::cascades_group::CascadesGroup; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(DeriveIden)] -pub enum LogicalProperty { - Table, - Id, - GroupId, - VariantTag, - Data, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(LogicalProperty::Table) - .if_not_exists() - .col(pk_auto(LogicalProperty::Id)) - .col(integer(LogicalProperty::GroupId)) - .foreign_key( - ForeignKey::create() - .from(LogicalProperty::Table, LogicalProperty::GroupId) - .to(CascadesGroup::Table, CascadesGroup::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(small_integer(LogicalProperty::VariantTag)) - .col(json(LogicalProperty::Data)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(LogicalProperty::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/memo/m20241029_000001_physical_children.rs b/optd-persistent/src/migrator/memo/m20241029_000001_physical_children.rs deleted file mode 100644 index 3983f0c..0000000 --- a/optd-persistent/src/migrator/memo/m20241029_000001_physical_children.rs +++ /dev/null @@ -1,70 +0,0 @@ -//! An entity representing the [`cascades_group`] children of every [`physical_expression`]. -//! -//! Formally, this entity is a junction which allows us to represent a many-to-many relationship -//! between [`physical_expression`] and [`cascades_group`]. Expressions can have any number of child -//! groups, and every group can be a child of many different expressions, hence the many-to-many -//! relationship. -//! -//! See [`cascades_group`] for more details. -//! -//! [`cascades_group`]: super::cascades_group -//! [`physical_expression`]: super::physical_expression - -use crate::migrator::memo::{ - cascades_group::CascadesGroup, physical_expression::PhysicalExpression, -}; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(DeriveIden)] -pub enum PhysicalChildren { - Table, - PhysicalExpressionId, - GroupId, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(PhysicalChildren::Table) - .if_not_exists() - .col(integer(PhysicalChildren::PhysicalExpressionId)) - .col(integer(PhysicalChildren::GroupId)) - .primary_key( - Index::create() - .col(PhysicalChildren::PhysicalExpressionId) - .col(PhysicalChildren::GroupId), - ) - .foreign_key( - ForeignKey::create() - .from( - PhysicalChildren::Table, - PhysicalChildren::PhysicalExpressionId, - ) - .to(PhysicalExpression::Table, PhysicalExpression::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .foreign_key( - ForeignKey::create() - .from(PhysicalChildren::Table, PhysicalChildren::GroupId) - .to(CascadesGroup::Table, CascadesGroup::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(PhysicalChildren::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/memo/m20241029_000001_physical_expression.rs b/optd-persistent/src/migrator/memo/m20241029_000001_physical_expression.rs deleted file mode 100644 index 8f7cb96..0000000 --- a/optd-persistent/src/migrator/memo/m20241029_000001_physical_expression.rs +++ /dev/null @@ -1,83 +0,0 @@ -//! An entity representing a logical plan expression in the Cascades framework. -//! -//! Quoted from the Microsoft article _Extensible query optimizers in practice_: -//! -//! > A physical expression is a tree of physical operators, which is also referred to as the -//! > _physical plan_ or simply _plan_. -//! -//! In the Cascades query optimization framework, the memo table stores equivalence classes of -//! expressions (see [`cascades_group`]). These equivalence classes, or "groups", store both -//! [`logical_expression`]s and `physical_expression`s. -//! -//! Optimization starts by exploring equivalent logical expressions within a group, and then it -//! proceeds to implement / optimize those logical operators into physical operators. For example, -//! the logical expression `Join(A, B)` could be implemented into a `HashJoin(A, B)` or a -//! `NestedLoopJoin(A, B)`, and both of these new physical expressions would be contained in the -//! same group. -//! -//! # Columns -//! -//! Each `physical_expression` has a unique primary key ID, and other tables will store a foreign -//! key reference to a specific `physical_expression`s. -//! -//! The more interesting column is the `fingerprint` column, in which we store a hashed fingerprint -//! value that can be used to efficiently check equality between two potentially equivalent physical -//! expressions (hash-consing). See ???TODO??? for more information on expression fingerprints. -//! -//! Finally, since there are many different types of operators, we store a variant tag and a data -//! column as JSON to represent the semi-structured data fields of logical operators. -//! -//! # Entity Relationships -//! -//! The only relationship that `physical_expression` has is to [`cascades_group`]. It has **both** a -//! one-to-many **and** a many-to-many relationship with [`cascades_group`], and you can see more -//! details about this in the module-level documentation for [`cascades_group`]. -//! -//! [`cascades_group`]: super::cascades_group -//! [`logical_expression`]: super::logical_expression - -use crate::migrator::memo::cascades_group::CascadesGroup; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(DeriveIden)] -pub enum PhysicalExpression { - Table, - Id, - GroupId, - Fingerprint, - VariantTag, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(PhysicalExpression::Table) - .if_not_exists() - .col(pk_auto(PhysicalExpression::Id)) - .col(integer(PhysicalExpression::GroupId)) - .foreign_key( - ForeignKey::create() - .from(PhysicalExpression::Table, PhysicalExpression::GroupId) - .to(CascadesGroup::Table, CascadesGroup::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(big_unsigned(PhysicalExpression::Fingerprint)) - .col(small_integer(PhysicalExpression::VariantTag)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(PhysicalExpression::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/memo/m20241029_000001_physical_property.rs b/optd-persistent/src/migrator/memo/m20241029_000001_physical_property.rs deleted file mode 100644 index 262ae41..0000000 --- a/optd-persistent/src/migrator/memo/m20241029_000001_physical_property.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! An entity representing a physical property of a physical expression in the Cascades framework. -//! -//! TODO what exactly are we storing in here? -//! TODO why is it linked to only physical expressions and not cascades groups? - -use crate::migrator::memo::physical_expression::PhysicalExpression; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(DeriveIden)] -pub enum PhysicalProperty { - Table, - Id, - PhysicalExpressionId, - VariantTag, - Data, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(PhysicalProperty::Table) - .if_not_exists() - .col(pk_auto(PhysicalProperty::Id)) - .col(integer(PhysicalProperty::PhysicalExpressionId)) - .foreign_key( - ForeignKey::create() - .from( - PhysicalProperty::Table, - PhysicalProperty::PhysicalExpressionId, - ) - .to(PhysicalExpression::Table, PhysicalExpression::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(small_integer(PhysicalProperty::VariantTag)) - .col(json(PhysicalProperty::Data)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(PhysicalProperty::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/memo/m20241029_000001_predicate.rs b/optd-persistent/src/migrator/memo/m20241029_000001_predicate.rs deleted file mode 100644 index b4a58b9..0000000 --- a/optd-persistent/src/migrator/memo/m20241029_000001_predicate.rs +++ /dev/null @@ -1,46 +0,0 @@ -/* -Table predicate { - id integer [pk] - data json - variant integer -} -*/ - -use sea_orm_migration::{ - prelude::*, - schema::{integer, json, pk_auto}, -}; - -#[derive(Iden)] -pub enum Predicate { - Table, - Id, - Data, - Variant, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(Predicate::Table) - .if_not_exists() - .col(pk_auto(Predicate::Id)) - .col(json(Predicate::Data)) - .col(integer(Predicate::Variant)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(Predicate::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/memo/m20241029_000001_predicate_children.rs b/optd-persistent/src/migrator/memo/m20241029_000001_predicate_children.rs deleted file mode 100644 index f3c0d10..0000000 --- a/optd-persistent/src/migrator/memo/m20241029_000001_predicate_children.rs +++ /dev/null @@ -1,61 +0,0 @@ -/* -Table predicate_children { - parent_id integer [ref: > predicate.id] - child_id integer [ref: > predicate.id] -} - */ - -use sea_orm_migration::{prelude::*, schema::integer}; - -use super::m20241029_000001_predicate::Predicate; - -#[derive(Iden)] -pub enum PredicateChildren { - Table, - ParentId, - ChildId, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(PredicateChildren::Table) - .if_not_exists() - .col(integer(PredicateChildren::ParentId)) - .foreign_key( - ForeignKey::create() - .from(PredicateChildren::Table, PredicateChildren::ParentId) - .to(Predicate::Table, Predicate::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(integer(PredicateChildren::ChildId)) - .foreign_key( - ForeignKey::create() - .from(PredicateChildren::Table, PredicateChildren::ChildId) - .to(Predicate::Table, Predicate::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .primary_key( - Index::create() - .col(PredicateChildren::ParentId) - .col(PredicateChildren::ChildId), - ) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(PredicateChildren::Table).to_owned()) - .await - } -} diff --git a/optd-persistent/src/migrator/memo/m20241029_000001_predicate_logical_expression_junction.rs b/optd-persistent/src/migrator/memo/m20241029_000001_predicate_logical_expression_junction.rs deleted file mode 100644 index ab901d6..0000000 --- a/optd-persistent/src/migrator/memo/m20241029_000001_predicate_logical_expression_junction.rs +++ /dev/null @@ -1,72 +0,0 @@ -/* -Table predicate_logical_expression_junction { - logical_expr_id integer [ref: > logical_expression.id] - predicate_id integer [ref: > predicate.id] -} - */ - -use sea_orm_migration::{prelude::*, schema::integer}; - -use super::{ - m20241029_000001_logical_expression::LogicalExpression, m20241029_000001_predicate::Predicate, -}; - -#[derive(Iden)] -pub enum PredicateLogicalExpressionJunction { - Table, - LogicalExprId, - PredicateId, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(PredicateLogicalExpressionJunction::Table) - .col(integer(PredicateLogicalExpressionJunction::LogicalExprId)) - .foreign_key( - ForeignKey::create() - .from( - PredicateLogicalExpressionJunction::Table, - PredicateLogicalExpressionJunction::LogicalExprId, - ) - .to(LogicalExpression::Table, LogicalExpression::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(integer(PredicateLogicalExpressionJunction::PredicateId)) - .foreign_key( - ForeignKey::create() - .from( - PredicateLogicalExpressionJunction::Table, - PredicateLogicalExpressionJunction::PredicateId, - ) - .to(Predicate::Table, Predicate::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .primary_key( - Index::create() - .col(PredicateLogicalExpressionJunction::LogicalExprId) - .col(PredicateLogicalExpressionJunction::PredicateId), - ) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table( - Table::drop() - .table(PredicateLogicalExpressionJunction::Table) - .to_owned(), - ) - .await - } -} diff --git a/optd-persistent/src/migrator/memo/m20241029_000001_predicate_physical_expression_junction.rs b/optd-persistent/src/migrator/memo/m20241029_000001_predicate_physical_expression_junction.rs deleted file mode 100644 index 8d82831..0000000 --- a/optd-persistent/src/migrator/memo/m20241029_000001_predicate_physical_expression_junction.rs +++ /dev/null @@ -1,74 +0,0 @@ -/* -Table predicate_physical_expression_junction { - physical_expr_id integer [ref: > physical_expression.id] - predicate_id integer [ref: > predicate.id] -} - */ - -use sea_orm_migration::{prelude::*, schema::integer}; - -use super::{ - m20241029_000001_physical_expression::PhysicalExpression, m20241029_000001_predicate::Predicate, -}; - -#[derive(Iden)] -pub enum PredicatePhysicalExpressionJunction { - Table, - PhysicalExprId, - PredicateId, -} - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(PredicatePhysicalExpressionJunction::Table) - .col(integer(PredicatePhysicalExpressionJunction::PhysicalExprId)) - .foreign_key( - ForeignKey::create() - .name("predicate_physical_expression_junction_physical_expr_id_fkey") - .from( - PredicatePhysicalExpressionJunction::Table, - PredicatePhysicalExpressionJunction::PhysicalExprId, - ) - .to(PhysicalExpression::Table, PhysicalExpression::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .col(integer(PredicatePhysicalExpressionJunction::PredicateId)) - .foreign_key( - ForeignKey::create() - .name("predicate_physical_expression_junction_predicate_id_fkey") - .from( - PredicatePhysicalExpressionJunction::Table, - PredicatePhysicalExpressionJunction::PredicateId, - ) - .to(Predicate::Table, Predicate::Id) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade), - ) - .primary_key( - Index::create() - .col(PredicatePhysicalExpressionJunction::PhysicalExprId) - .col(PredicatePhysicalExpressionJunction::PredicateId), - ) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table( - Table::drop() - .table(PredicatePhysicalExpressionJunction::Table) - .to_owned(), - ) - .await - } -} diff --git a/optd-persistent/src/migrator/memo/mod.rs b/optd-persistent/src/migrator/memo/mod.rs deleted file mode 100644 index b4caabf..0000000 --- a/optd-persistent/src/migrator/memo/mod.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! Entities related to the memo table used for dynamic programming in the Cascades query -//! optimization framework. - -pub(crate) mod m20241029_000001_cascades_group; -pub(crate) mod m20241029_000001_group_winner; -pub(crate) mod m20241029_000001_logical_children; -pub(crate) mod m20241029_000001_logical_expression; -pub(crate) mod m20241029_000001_logical_property; -pub(crate) mod m20241029_000001_physical_children; -pub(crate) mod m20241029_000001_physical_expression; -pub(crate) mod m20241029_000001_physical_property; -pub(crate) mod m20241029_000001_predicate; -pub(crate) mod m20241029_000001_predicate_children; -pub(crate) mod m20241029_000001_predicate_logical_expression_junction; -pub(crate) mod m20241029_000001_predicate_physical_expression_junction; - -pub(crate) use m20241029_000001_cascades_group as cascades_group; -pub(crate) use m20241029_000001_group_winner as group_winner; -pub(crate) use m20241029_000001_logical_children as logical_children; -pub(crate) use m20241029_000001_logical_expression as logical_expression; -pub(crate) use m20241029_000001_logical_property as logical_property; -pub(crate) use m20241029_000001_physical_children as physical_children; -pub(crate) use m20241029_000001_physical_expression as physical_expression; -pub(crate) use m20241029_000001_physical_property as physical_property; -pub(crate) use m20241029_000001_predicate as predicate; -pub(crate) use m20241029_000001_predicate_children as predicate_children; -pub(crate) use m20241029_000001_predicate_logical_expression_junction as predicate_logical_expression_junction; -pub(crate) use m20241029_000001_predicate_physical_expression_junction as predicate_physical_expression_junction; diff --git a/optd-persistent/src/migrator/mod.rs b/optd-persistent/src/migrator/mod.rs deleted file mode 100644 index 468f7d0..0000000 --- a/optd-persistent/src/migrator/mod.rs +++ /dev/null @@ -1,42 +0,0 @@ -use sea_orm_migration::prelude::*; - -mod catalog; -mod cost_model; -mod memo; - -pub struct Migrator; - -#[async_trait::async_trait] -impl MigratorTrait for Migrator { - fn migrations() -> Vec> { - vec![ - Box::new(catalog::database_metadata::Migration), - Box::new(catalog::namespace_metadata::Migration), - Box::new(catalog::table_metadata::Migration), - Box::new(catalog::attribute::Migration), - Box::new(catalog::attribute_constraint_junction::Migration), - Box::new(catalog::attribute_foreign_constraint_junction::Migration), - Box::new(catalog::index_metadata::Migration), - Box::new(catalog::trigger::Migration), - Box::new(catalog::constraint_metadata::Migration), - Box::new(cost_model::statistic::Migration), - Box::new(cost_model::versioned_statistic::Migration), - Box::new(cost_model::statistic_to_attribute_junction::Migration), - Box::new(cost_model::physical_expression_to_statistic_junction::Migration), - Box::new(cost_model::event::Migration), - Box::new(cost_model::plan_cost::Migration), - Box::new(memo::cascades_group::Migration), - Box::new(memo::group_winner::Migration), - Box::new(memo::logical_expression::Migration), - Box::new(memo::logical_children::Migration), - Box::new(memo::logical_property::Migration), - Box::new(memo::physical_expression::Migration), - Box::new(memo::physical_children::Migration), - Box::new(memo::physical_property::Migration), - Box::new(memo::predicate::Migration), - Box::new(memo::predicate_children::Migration), - Box::new(memo::predicate_logical_expression_junction::Migration), - Box::new(memo::predicate_physical_expression_junction::Migration), - ] - } -} diff --git a/schema/all_tables.dbml b/schema/all_tables.dbml deleted file mode 100644 index 29a2136..0000000 --- a/schema/all_tables.dbml +++ /dev/null @@ -1,216 +0,0 @@ -// Use DBML to define your database structure -// Docs: https://dbml.dbdiagram.io/docs - -Table database_metadata { - id integer PK - name varchar - creation_time timestamp -} - -Table namespace_metadata { - id integer PK - database_id integer [ref: > database_metadata.id] - name varchar - creation_time timestamp -} - -Table table_metadata { - id integer PK - namespace_id integer [ref: > namespace_metadata.id] - name varchar - creation_time timestamp -} - -Table attribute { - id integer PK // global index - table_id integer [ref: > table_metadata.id] - name varchar - compression_method char - variant_tag integer // Data type of this attribute. Should we make another table to explain the type mapping? - base_attribute_number integer // local index within the table - is_not_null boolean // physical property -} - -Table statistic { - id integer PK - name varchar - table_id integer [ref: > table_metadata.id, null] // null if not a table statistic - creation_time timestamp - number_of_attributes integer // 0 if a table statistic - variant_tag integer // Should we make another table to explain the type mapping? - description varchar // Store the sorted attribute ids of this statistic, to support quick lookup (OR we can use junction table to look up) -} - -Table versioned_statistic { - id integer PK - epoch_id integer [ref: > event.epoch_id] - statistic_id integer [ref: > statistic.id] - statistic_value json -} - -Table event { - epoch_id integer PK - source_variant varchar - create_timestamp timestamp - data json -} - -Table plan_cost { - id integer PK - physical_expression_id integer [ref: > physical_expression.id] - epoch_id integer [ref: > event.epoch_id] - // It is json type, including computation cost, I/O cost, etc. - cost json [null] - // Raw estimated output row count of this expression - estimated_statistic float [null] - // Whether the cost is valid or not. If the latest cost for an expr is invalid, then we need to recompute the cost. - // We need to invalidate the cost when the related stats are updated. - is_valid boolean -} - -Table index { - id integer PK - name varchar - table_id integer [ref: > table_metadata.id] - // Whether it is an unique index. - is_unique boolean - // Only valid for unique index, if true, then null value is equal, if false, null value is distinct. - nulls_not_distinct boolean - // Whether the attribute is primary. - is_primary boolean - // If true, the table was last clustered on this index - is_clustered boolean - // More fields might be added in the future for expressiveness on exclusion constraint. - is_exclusion boolean - // Do we need it? - number_of_attributes integer - // Stores the related attribute ids. - data json -} - -Table trigger { - id integer PK - name varchar - table_id integer [ref: > table_metadata.id] - // This field is only valid if it is triggered by another parent trigger. - parent_trigger_id integer [ref: > trigger.id] - function json -} - -// Not-null is handled directly in `attribute`. See `is_not_null` field. -// Constraint trigger is handled directly in `trigger`. -Table constraint { - id integer PK - name varchar - variant_tag integer // pk, fk, unique, check, exclusion - table_id integer [ref: > table_metadata.id, null] // null if not a table constraint - index_id integer [ref: > index.id, null] // The index supporting this constraint, if it's a unique, primary key, foreign key, or exclusion constraint; else null - foreign_ref_id integer [ref: > table_metadata.id, null] // If a foreign key, the referenced table; else null - check_src varchar // the expression tree for a check constraint, which provides a textual representation of the constraint expression -} - -// The constrained attributes (columns) if a constraint is a table constraint (including foreign keys, but not constraint triggers) -Table attribute_constraint_junction { - attribute_id integer [ref: > attribute.id] - constraint_id integer [ref: > constraint.id] -} - -// The referenced attributes (columns) if the constraint is a foreign key -Table attribute_foreign_constraint_junction { - attribute_id integer [ref: > attribute.id] - constraint_id integer [ref: > constraint.id] -} - -Table statistic_to_attribute_junction { - statistic_id integer [ref: > statistic.id] - attribute_id integer [ref: > attribute.id] -} - -Table physical_expression_to_statistic_junction { - physical_expression_id integer [ref: > physical_expression.id] - statistic_id integer [ref: > statistic.id] -} - -// Logical expressions and groups -Table logical_expression { - id integer [pk] - group_id integer [ref: > cascades_group.id] - fingerprint integer - variant_tag integer - predicate integer [ref: > predicate.id] -} - -Table cascades_group { - id integer [pk] - latest_winner integer [ref: > physical_expression.id, null] - in_progress boolean - is_optimized boolean - parent integer [ref: > cascades_group.id] -} - -// Physical expressions and properties -Table physical_expression { - id integer [pk] - group_id integer [ref: > cascades_group.id] - fingerprint integer - variant_tag integer - predicate integer [ref: > predicate.id] -} - -Table physical_property { - id integer [pk] - physical_expression_id integer [ref: > physical_expression.id] - variant_tag integer - data json -} - -// Junction tables -Table logical_group_junction { - group_id integer [ref: > cascades_group.id] - logical_expression_id integer [ref: > logical_expression.id] -} - -Table physical_group_junction { - group_id integer [ref: > cascades_group.id] - physical_expression_id integer [ref: > physical_expression.id] -} - -// Properties -Table logical_property { - id integer [pk] - group_id integer [ref: > cascades_group.id] - variant_tag integer - data json -} - -// Winners tracking -Table group_winner { - id integer [pk] - group_id integer [ref: > cascades_group.id] - physical_expression_id integer [ref: > physical_expression.id] - cost integer - epoch_id integer [ref: > event.epoch_id] -} - -Table predicate { - id integer [pk] - data json - variant integer -} - -Table predicate_children { - parent_id integer [ref: > predicate.id] - child_id integer [ref: > predicate.id] -} - -Table predicate_logical_expression_junction { - logical_expr_id integer [ref: > logical_expression.id] - predicate_id integer [ref: > predicate.id] -} - -Table predicate_physical_expression_junction { - physical_expr_id integer [ref: > physical_expression.id] - predicate_id integer [ref: > predicate.id] -} - - From a595c576a26d8f24571d57e1fccb9eec0317a0d5 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Wed, 27 Nov 2024 10:55:19 -0500 Subject: [PATCH 02/10] add boilerplate files This commit adds the boilerplate files and fixes the CI workflows. --- .github/workflows/check.yml | 19 - .github/workflows/test.yml | 3 - .gitignore | 13 + Cargo.lock | 2836 +++++++++++++++++++++++++++++++++++ optd-mvp/Cargo.toml | 27 + optd-mvp/README.md | 27 + optd-mvp/src/bin/migrate.rs | 19 + optd-mvp/src/lib.rs | 16 + optd-mvp/src/main.rs | 3 + 9 files changed, 2941 insertions(+), 22 deletions(-) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 optd-mvp/Cargo.toml create mode 100644 optd-mvp/README.md create mode 100644 optd-mvp/src/bin/migrate.rs create mode 100644 optd-mvp/src/lib.rs create mode 100644 optd-mvp/src/main.rs diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 33cf544..aced2f1 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -21,9 +21,6 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true -defaults: - run: - working-directory: ./optd-persistent name: check jobs: fmt: @@ -61,22 +58,6 @@ jobs: components: clippy - name: cargo clippy run: cargo clippy --locked --all-targets --all-features -- -D warnings - doc: - # run docs generation on nightly rather than stable. This enables features like - # https://doc.rust-lang.org/beta/unstable-book/language-features/doc-cfg.html which allows an - # API be documented as only available in some specific platforms. - runs-on: ubuntu-latest - name: nightly / doc - steps: - - uses: actions/checkout@v4 - with: - submodules: true - - name: Install nightly - uses: dtolnay/rust-toolchain@nightly - - name: Install cargo-docs-rs - uses: dtolnay/install@cargo-docs-rs - - name: cargo docs-rs - run: cargo docs-rs hack: # cargo-hack checks combinations of feature flags to ensure that features are all additive # which is required for feature unification diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d6253a0..565c9eb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,9 +17,6 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true -defaults: - run: - working-directory: ./optd-persistent name: test jobs: required: diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..861fce8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Contains compiled files and executables. +debug/ +target/ + +# These are backup files generated by rustfmt. +**/*.rs.bk + +# Ignore any database files. +**/*.db + +# We will check in all code-generated entity files, as newer versions of `sea-orm-cli` might +# conflict with previous versions. +# **/entities \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..3059383 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2836 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aa100a6f6f525226719f8de3f70076be4f4191801ebd92621450d1c51e9053d" + +[[package]] +name = "ahash" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + +[[package]] +name = "allocator-api2" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52f4a9cf8f3ff707b4eb1acd0136efd8b3bec6b345ed32fcab47c0a5c99b800" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + +[[package]] +name = "arrayvec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a2f58b0bb10c380af2b26e57212856b8c9a59e0925b4c20f4a174a49734eaf7" + +[[package]] +name = "async-stream" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a26cb53174ddd320edfff199a853f93d571f48eeb4dde75e67a9a3dbb7b7e5e" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db134ba52475c060f3329a8ef0f8786d6b872ed01515d4b79c162e5798da1340" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.98", +] + +[[package]] +name = "async-trait" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6287685011f026b98d26afd53251ad0101e856531b423eb2384265f7d4f5b01" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.98", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "backtrace" +version = "0.3.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88fb5a785d6b44fd9d6700935608639af1b8356de1e55d5f7c2740f4faa15d82" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "base64" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" + +[[package]] +name = "base64ct" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71acf5509fc522cce1b100ac0121c635129bfd4d91cdf036bcc9b9935f97ccf5" + +[[package]] +name = "bigdecimal" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5274a6b6e0ee020148397245b973e30163b7bffbc6d473613f850cb99888581e" +dependencies = [ + "libm", + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "bitflags" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03588e54c62ae6d763e2a80090d50353b785795361b4ff5b3bf0a5097fc31c0b" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cc" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets 0.52.0", +] + +[[package]] +name = "clap" +version = "4.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384e169cc618c613d5e3ca6404dda77a8685a63e08660dcc64abaf7da7cb0c7a" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef137bbe35aab78bdb468ccfba75a5f4d8321ae011d34063770780545176af2d" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "codespan-reporting" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6ce42b8998a383572e0a802d859b1f00c79b7b7474e62fff88ee5c2845d9c13" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "concurrent-queue" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff" + +[[package]] +name = "crossbeam-queue" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cxx" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c53d75fe543215ca091d792e13351dcb940842dd2829b2a2dd43ab4bd1a015" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "618f85c8f132bd8912aab124e15a38adc762bb7e3cef84524adde1692ef3e8bc" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn 1.0.98", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca21461be76a23df4f63a2107a0bb406ef41548e635ff7edcbd1ab5a6bb997e2" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8da0a2c0697647b5824844a5d2dedcd97a2d7b75e6e4d0b8dd183e4081e1cf" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.98", +] + +[[package]] +name = "darling" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c99d16b88c92aef47e58dadd53e87b4bd234c29934947a6cec8b466300f99b" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ea05d2fcb27b53f7a98faddaf5f2914760330ab7703adfc9df13332b42189f9" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "darling_macro" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bfb82b62b1b8a2a9808fb4caf844ede819a76cfc23b2827d7f94eefb49551eb" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "der" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19c5cb402c5c958281c7c0702edea7b780d03b86b606497ca3a10fcd3fc393ac" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +dependencies = [ + "powerfmt", + "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 = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dotenvy" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9155c8f4dc55c7470ae9da3f63c6785245093b3f6aeb0f5bf2e968efbba314" +dependencies = [ + "dirs", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +dependencies = [ + "serde", +] + +[[package]] +name = "equivalent" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "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 = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[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 0.9.8", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fa4cc29d25b0687b8570b0da86eac698dcb525110ad8b938fe6712baa711ec" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a" +dependencies = [ + "futures-core", + "futures-task", + "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.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +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.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692eaaf7f7607518dd3cef090f1474b61edc5301d8012f09579920df68b725ee" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f41e9c77b6fc05b57497b960aad55942a9bbc5b20e1e623cf7fb1868f695d1" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddca131f3e7f2ce2df364b57949a9d47915cfbd35e46cfee355ccebbf794d6a2" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inherent" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c05a410d53e44fc943a35a32ca27e32af2ea004d5107ccef685d022fc2b9fb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.98", +] + +[[package]] +name = "is-terminal" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24fddda5af7e54bf7da53067d6e802dbcc381d0a8eef629df528e3ebf68755cb" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92a9df60778f789c37f76778ae8d0a2471c41baa8b059d98a5873c978f549587" + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "js-sys" +version = "0.3.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" +dependencies = [ + "spin 0.4.10", +] + +[[package]] +name = "libc" +version = "0.2.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" + +[[package]] +name = "libm" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dfb9f65d9966f6ca6522043978030b564f3291af987fbf1dd55b6a064ba1b36" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "lock_api" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matches" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e" + +[[package]] +name = "md-5" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6a38fc55c8bbc10058782919516f88826e70320db6d206aebc49611d24216ae" +dependencies = [ + "digest", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "minimal-lexical" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6595bb28ed34f43c3fe088e48f6cfb2e033cab45f25a5384d5fdf564fbc8c4b2" + +[[package]] +name = "miniz_oxide" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be0f75932c1f6cfae3c04000e40114adf955636e19040f9c0a2c380702aa1c7f" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.45.0", +] + +[[package]] +name = "nom" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1" +dependencies = [ + "memchr", + "minimal-lexical", + "version_check", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2399c9463abc5f909349d8aa9ba080e0b88b3ce2885389b60b993f39b1a56905" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[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.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" +dependencies = [ + "libc", +] + +[[package]] +name = "object" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "optd-mvp" +version = "0.1.0" +dependencies = [ + "async-stream", + "async-trait", + "sea-orm", + "sea-orm-migration", + "serde", + "serde_json", + "strum", + "thiserror 2.0.0", + "tokio", + "trait-variant", +] + +[[package]] +name = "ordered-float" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d84eb1409416d254e4a9c8fa56cc24701755025b458f0fcd8e59e1f5f40c23bf" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ouroboros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c86de06555b970aec45229b27291b53154f21a5743a163419f4e4c0b065dcde" +dependencies = [ + "aliasable", + "ouroboros_macro", + "static_assertions", +] + +[[package]] +name = "ouroboros_macro" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3633d65683f13b9bcfaa3150880b018899fb0e5d0542f4adaea4f503fdb5eabf" +dependencies = [ + "heck 0.4.1", + "itertools 0.12.0", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2f4f894f3865f6c0e02810fc597300f34dc2510f66400da262d8ae10e75767d" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.29.0", +] + +[[package]] +name = "paste" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" + +[[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.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project-lite" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" + +[[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 = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" + +[[package]] +name = "proc-macro-error" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d259aa4825fa1a2371419d30a520219feff9fb3591550a209b4477d2ebaae4f" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.98", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd21889899aa8e1ca2b924c1d3f08086631fc90768225b3268b5d5c3e806a503" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.98", + "syn-mid", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "606c4ba35817e2922a308af55ad51bab3645b59eae5c570d4a6cf07e36bd493b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "version_check", + "yansi", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" +dependencies = [ + "bitflags 1.1.0", +] + +[[package]] +name = "redox_users" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +dependencies = [ + "getrandom", + "redox_syscall", +] + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72457500f2cf948feb4efccaeb460570c8f66ee5ba33c936bb4bfaa628d71853" +dependencies = [ + "byteorder", + "regex-syntax", + "utf8-ranges", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "ring" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb9d44f9bf6b635117787f72416783eb7e4227aaf255e5ce739563d817176a7e" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted", + "windows-sys 0.48.0", +] + +[[package]] +name = "rsa" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dd2017d3e6d67384f301f8b06fbf4567afc576430a61624d845eb04d2b30a72" +dependencies = [ + "byteorder", + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "subtle", + "zeroize", +] + +[[package]] +name = "rust_decimal" +version = "1.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee9164faf726e4f3ece4978b25ca877ddc6802fa77f38cdccb32c7f805ecd70c" +dependencies = [ + "arrayvec", + "num-traits", + "serde", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95" + +[[package]] +name = "rustix" +version = "0.38.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4828ea528154ae444e5a642dbb7d5623354030dc9822b83fd9bb79683c7399d0" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e4980fa29e4c4b212ffb3db068a564cbf560e51d3944b7c88bd8bf5bec64f4" +dependencies = [ + "base64 0.21.0", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + +[[package]] +name = "rustls-webpki" +version = "0.102.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scratch" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e114536316b51a5aa7a0e59fc49661fd263c5507dd08bd28de052e57626ce69" + +[[package]] +name = "sea-bae" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd3534a9978d0aa7edd2808dc1f8f31c4d0ecd31ddf71d997b3c98e9f3c9114" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "sea-orm" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5680a8b686985116607ef5f5af2b1f9e1cc2c228330e93101816a0baa279afa" +dependencies = [ + "async-stream", + "async-trait", + "bigdecimal", + "chrono", + "futures", + "log", + "ouroboros", + "rust_decimal", + "sea-orm-macros", + "sea-query", + "sea-query-binder", + "serde", + "serde_json", + "sqlx", + "strum", + "thiserror 1.0.35", + "time", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "sea-orm-cli" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aefbd960c9ed7b2dfbab97b11890f5d8c314ad6e2f68c7b36c73ea0967fcc25" +dependencies = [ + "chrono", + "clap", + "dotenvy", + "glob", + "regex", + "sea-schema", + "tracing", + "tracing-subscriber", + "url", +] + +[[package]] +name = "sea-orm-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a239e3bb1b566ad4ec2654d0d193d6ceddfd733487edc9c21a64d214c773910" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "sea-bae", + "syn 2.0.87", + "unicode-ident", +] + +[[package]] +name = "sea-orm-migration" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa7bbfbe3bec60b5925193acc9c98b9f8ae9853f52c8004df0c1ea5193c01ea0" +dependencies = [ + "async-trait", + "clap", + "dotenvy", + "futures", + "sea-orm", + "sea-orm-cli", + "sea-schema", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "sea-query" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff504d13b5e4b52fffcf2fb203d0352a5722fa5151696db768933e41e1e591bb" +dependencies = [ + "bigdecimal", + "chrono", + "inherent", + "ordered-float", + "rust_decimal", + "sea-query-derive", + "serde_json", + "time", + "uuid", +] + +[[package]] +name = "sea-query-binder" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0019f47430f7995af63deda77e238c17323359af241233ec768aba1faea7608" +dependencies = [ + "bigdecimal", + "chrono", + "rust_decimal", + "sea-query", + "serde_json", + "sqlx", + "time", + "uuid", +] + +[[package]] +name = "sea-query-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9834af2c4bd8c5162f00c89f1701fb6886119a88062cf76fe842ea9e232b9839" +dependencies = [ + "darling", + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.87", + "thiserror 1.0.35", +] + +[[package]] +name = "sea-schema" +version = "0.16.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a4ff9e87c4340affbec4f7790d724dcd87e71fcd0ffe2247481843380485aa" +dependencies = [ + "futures", + "sea-query", + "sea-schema-derive", +] + +[[package]] +name = "sea-schema-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "debdc8729c37fdbf88472f97fd470393089f997a909e535ff67c544d18cfccf0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "serde" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "serde_json" +version = "1.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" +dependencies = [ + "itoa 1.0.1", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa 0.4.0", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77f4e7f65455545c2153c1253d25056825e77ee2533f0e41deb65a93a34852f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signature" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "spin" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceac490aa12c567115b40b7b7fceca03a6c9d53d5defea066123debc83c5dc1f" + +[[package]] +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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a5be806ab6f127c3da44b7378837ebf01dadca8510a0e572460216b228bd0e" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlformat" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a" +dependencies = [ + "itertools 0.10.0", + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93334716a037193fac19df402f8571269c84a00852f6a7066b5d2616dcd64d3e" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d8060b456358185f7d50c55d9b5066ad956956fddec42ee2e8567134a8936e" +dependencies = [ + "atoi", + "bigdecimal", + "byteorder", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown", + "hashlink", + "hex", + "indexmap", + "log", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "rust_decimal", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlformat", + "thiserror 1.0.35", + "time", + "tokio", + "tokio-stream", + "tracing", + "url", + "uuid", + "webpki-roots", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 2.0.87", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1804e8a7c7865599c9c79be146dc8a9fd8cc86935fa641d3ea58e5f0688abaa5" +dependencies = [ + "dotenvy", + "either", + "heck 0.5.0", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 2.0.87", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" +dependencies = [ + "atoi", + "base64 0.22.0", + "bigdecimal", + "bitflags 2.4.0", + "byteorder", + "bytes", + "chrono", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa 1.0.1", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand", + "rsa", + "rust_decimal", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 1.0.35", + "time", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" +dependencies = [ + "atoi", + "base64 0.22.0", + "bigdecimal", + "bitflags 2.4.0", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa 1.0.1", + "log", + "md-5", + "memchr", + "num-bigint", + "once_cell", + "rand", + "rust_decimal", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 1.0.35", + "time", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680" +dependencies = [ + "atoi", + "chrono", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "time", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stringprep" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117c413ac8a6cc19c773939932477a341e416eff7f0e84db42f091d85d7c6e0e" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-mid" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.98", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "termcolor" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a52c023823933499250b43960b272e25336c6e2ab8684672edc34489f049ccdd" +dependencies = [ + "wincolor", +] + +[[package]] +name = "thiserror" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85" +dependencies = [ + "thiserror-impl 1.0.35", +] + +[[package]] +name = "thiserror" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15291287e9bff1bc6f9ff3409ed9af665bec7a5fc8ac079ea96be07bca0e2668" +dependencies = [ + "thiserror-impl 2.0.0", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.98", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22efd00f33f93fa62848a7cab956c3d38c8d43095efda1decfc2b3a5dc0b8972" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa 1.0.1", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tokio" +version = "1.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3ce25f50619af8b0aec2eb23deebe84249e19e2ddd393a6e16e3300a6dadfd" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "tokio-stream" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.98", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "once_cell", + "regex", + "sharded-slab", + "thread_local", + "tracing", + "tracing-core", +] + +[[package]] +name = "trait-variant" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "typenum" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" + +[[package]] +name = "unicode-bidi" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2560b941fdb9ea38301b9b708504d612fcdf9c91a8c31d82219bd74cb07d304d" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-ident" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" + +[[package]] +name = "unicode-normalization" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f" + +[[package]] +name = "unicode-width" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc85732b6d55a0d520aaf765536a188d9d993770c28633422f85bb646da61335" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "utf8-ranges" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "uuid" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" +dependencies = [ + "serde", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn 1.0.98", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.98", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" + +[[package]] +name = "web-sys" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721c6263e2c66fd44501cc5efbfa2b7dfa775d13e4ea38c46299646ed1f9c70a" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de2cfda980f21be5a7ed2eadb3e6fe074d56022bea2cdeb1a62eb220fc04188" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "whoami" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524b58fa5a20a2fb3014dd6358b70e6579692a56ef6fce928834e488f42f65e8" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "winapi" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3ad91d846a4a5342c1fb7008d26124ee6cf94a3953751618577295373b32117" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a16a8e2ebfc883e2b1771c6482b1fb3c6831eab289ba391619a2d93a7356220f" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca29cb03c8ceaf20f8224a18a530938305e9872b1478ea24ff44b4f503a1d1d" + +[[package]] +name = "wincolor" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9dc3aa9dcda98b5a16150c54619c1ead22e3d3a5d458778ae914be760aa981a" +dependencies = [ + "winapi", +] + +[[package]] +name = "windows" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceb069ac8b2117d36924190469735767f0990833935ab430155e71a44bafe148" +dependencies = [ + "windows_aarch64_msvc 0.29.0", + "windows_i686_gnu 0.29.0", + "windows_i686_msvc 0.29.0", + "windows_x86_64_gnu 0.29.0", + "windows_x86_64_msvc 0.29.0", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d027175d00b01e0cbeb97d6ab6ebe03b12330a35786cbaca5252b1c4bf5d9b" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8793f59f7b8e8b01eda1a652b2697d87b93097198ae85f823b969ca5b89bba58" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8602f6c418b67024be2996c512f5f995de3ba417f4c75af68401ab8756796ae4" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d615f419543e0bd7d2b3323af0d86ff19cbc4f816e6453f36a2c2ce889c354" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d95421d9ed3672c280884da53201a5c46b7b2765ca6faf34b0d71cf34a3561" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "yansi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" + +[[package]] +name = "zerocopy" +version = "0.7.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/optd-mvp/Cargo.toml b/optd-mvp/Cargo.toml new file mode 100644 index 0000000..3b72407 --- /dev/null +++ b/optd-mvp/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "optd-mvp" +version = "0.1.0" +edition = "2021" +authors = ["Connor Tsui"] + +[dependencies] + +# `VARIANTS` constant made public in "1.1.1". +sea-orm = { version = "1.1.1", features = [ + "sqlx-sqlite", + "runtime-tokio-rustls", + "macros", + "debug-print", + "with-json", +] } +sea-orm-migration = "1.0" +serde = "1.0" +serde_json = "1.0.118" # Support `Hash` on `serde_json::Value` in "1.0.118". +tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } +trait-variant = "0.1.2" # Support `make(Send)` syntax in "0.1.2". +thiserror = "2.0" + +# Pin more recent versions for `-Zminimal-versions`. +async-trait = "0.1.43" # Remove lifetime parameter from "0.1.42". +async-stream = "0.3.1" # Fix unsatisfied trait bound from "0.3.0". +strum = "0.26.0" # Fix `std::marker::Sized` from "0.25.0". diff --git a/optd-mvp/README.md b/optd-mvp/README.md new file mode 100644 index 0000000..cfd082a --- /dev/null +++ b/optd-mvp/README.md @@ -0,0 +1,27 @@ +# Generate the `entities` module + +To make changes to the database tables and schema, you will have to modify files in the `migrator` module and then update the `entities` module using `sea-orm-cli`. + +This assumes that you already have the `sqlite3` binary installed. First, make sure you have installed `sea-orm-cli`: + +```sh +$ cargo install sea-orm-cli +``` + +Make sure your working directory is in the crate root: + +```sh +$ cd optd-mvp +``` + +If you have not generate the `sqlite.db` file yet, you will need to run this command which will generate the `sqlite.db` file and run all of the migrations: + +```sh +$ cargo run --bin migrate +``` + +Finally, run this command to generate / overwrite the `entities` module in the `src` directory. + +```sh +$ sea-orm-cli generate entity -u sqlite:./sqlite.db -o src/entities +``` diff --git a/optd-mvp/src/bin/migrate.rs b/optd-mvp/src/bin/migrate.rs new file mode 100644 index 0000000..0634f23 --- /dev/null +++ b/optd-mvp/src/bin/migrate.rs @@ -0,0 +1,19 @@ +//! A simple script that generates the database file needed for `sea-orm-cli` to extract the schemas +//! from and generate the `entities` module. + +use optd_mvp::{migrate, DATABASE_FILENAME, DATABASE_URL}; +use sea_orm::*; +use sea_orm_migration::prelude::*; + +#[tokio::main] +async fn main() { + let _ = std::fs::remove_file(DATABASE_FILENAME); + + let db = Database::connect(DATABASE_URL) + .await + .expect("Unable to connect to the database"); + + migrate(&db) + .await + .expect("Something went wrong during migration"); +} diff --git a/optd-mvp/src/lib.rs b/optd-mvp/src/lib.rs new file mode 100644 index 0000000..5abd59f --- /dev/null +++ b/optd-mvp/src/lib.rs @@ -0,0 +1,16 @@ +use sea_orm::*; +use sea_orm_migration::prelude::*; + +mod migrator; +use migrator::Migrator; + +mod entities; + +/// The filename of the SQLite database for migration. +pub const DATABASE_FILENAME: &str = "sqlite.db"; +/// The URL of the SQLite database for migration. +pub const DATABASE_URL: &str = "sqlite:./sqlite.db?mode=rwc"; + +pub async fn migrate(db: &DatabaseConnection) -> Result<(), DbErr> { + Migrator::refresh(db).await +} diff --git a/optd-mvp/src/main.rs b/optd-mvp/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/optd-mvp/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} From 06495a125628101d824891a277ea5558eea3d9a0 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Wed, 27 Nov 2024 11:03:32 -0500 Subject: [PATCH 03/10] add migration and entity files --- optd-mvp/src/entities/cascades_group.rs | 76 +++++++++++ optd-mvp/src/entities/logical_children.rs | 46 +++++++ optd-mvp/src/entities/logical_expression.rs | 49 +++++++ optd-mvp/src/entities/mod.rs | 9 ++ optd-mvp/src/entities/physical_children.rs | 46 +++++++ optd-mvp/src/entities/physical_expression.rs | 49 +++++++ optd-mvp/src/entities/prelude.rs | 9 ++ .../memo/m20241127_000001_cascades_group.rs | 123 ++++++++++++++++++ .../memo/m20241127_000001_logical_children.rs | 65 +++++++++ .../m20241127_000001_logical_expression.rs | 84 ++++++++++++ .../m20241127_000001_physical_children.rs | 70 ++++++++++ .../m20241127_000001_physical_expression.rs | 85 ++++++++++++ optd-mvp/src/migrator/memo/mod.rs | 14 ++ optd-mvp/src/migrator/mod.rs | 18 +++ 14 files changed, 743 insertions(+) create mode 100644 optd-mvp/src/entities/cascades_group.rs create mode 100644 optd-mvp/src/entities/logical_children.rs create mode 100644 optd-mvp/src/entities/logical_expression.rs create mode 100644 optd-mvp/src/entities/mod.rs create mode 100644 optd-mvp/src/entities/physical_children.rs create mode 100644 optd-mvp/src/entities/physical_expression.rs create mode 100644 optd-mvp/src/entities/prelude.rs create mode 100644 optd-mvp/src/migrator/memo/m20241127_000001_cascades_group.rs create mode 100644 optd-mvp/src/migrator/memo/m20241127_000001_logical_children.rs create mode 100644 optd-mvp/src/migrator/memo/m20241127_000001_logical_expression.rs create mode 100644 optd-mvp/src/migrator/memo/m20241127_000001_physical_children.rs create mode 100644 optd-mvp/src/migrator/memo/m20241127_000001_physical_expression.rs create mode 100644 optd-mvp/src/migrator/memo/mod.rs create mode 100644 optd-mvp/src/migrator/mod.rs diff --git a/optd-mvp/src/entities/cascades_group.rs b/optd-mvp/src/entities/cascades_group.rs new file mode 100644 index 0000000..9c2ba83 --- /dev/null +++ b/optd-mvp/src/entities/cascades_group.rs @@ -0,0 +1,76 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "cascades_group")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub winner: Option, + pub cost: Option, + pub is_optimized: bool, + pub parent_id: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "Entity", + from = "Column::ParentId", + to = "Column::Id", + on_update = "Cascade", + on_delete = "SetNull" + )] + SelfRef, + #[sea_orm(has_many = "super::logical_children::Entity")] + LogicalChildren, + #[sea_orm(has_many = "super::logical_expression::Entity")] + LogicalExpression, + #[sea_orm(has_many = "super::physical_children::Entity")] + PhysicalChildren, + #[sea_orm( + belongs_to = "super::physical_expression::Entity", + from = "Column::Winner", + to = "super::physical_expression::Column::Id", + on_update = "Cascade", + on_delete = "SetNull" + )] + PhysicalExpression, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::LogicalChildren.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::PhysicalChildren.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::logical_children::Relation::LogicalExpression.def() + } + fn via() -> Option { + Some(super::logical_children::Relation::CascadesGroup.def().rev()) + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::physical_children::Relation::PhysicalExpression.def() + } + fn via() -> Option { + Some( + super::physical_children::Relation::CascadesGroup + .def() + .rev(), + ) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-mvp/src/entities/logical_children.rs b/optd-mvp/src/entities/logical_children.rs new file mode 100644 index 0000000..120641f --- /dev/null +++ b/optd-mvp/src/entities/logical_children.rs @@ -0,0 +1,46 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "logical_children")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub logical_expression_id: i32, + #[sea_orm(primary_key, auto_increment = false)] + pub group_id: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::cascades_group::Entity", + from = "Column::GroupId", + to = "super::cascades_group::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + CascadesGroup, + #[sea_orm( + belongs_to = "super::logical_expression::Entity", + from = "Column::GroupId", + to = "super::logical_expression::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + LogicalExpression, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::CascadesGroup.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::LogicalExpression.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-mvp/src/entities/logical_expression.rs b/optd-mvp/src/entities/logical_expression.rs new file mode 100644 index 0000000..1e85d1d --- /dev/null +++ b/optd-mvp/src/entities/logical_expression.rs @@ -0,0 +1,49 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "logical_expression")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub group_id: i32, + pub fingerprint: i64, + pub kind: i16, + pub data: Json, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::cascades_group::Entity", + from = "Column::GroupId", + to = "super::cascades_group::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + CascadesGroup, + #[sea_orm(has_many = "super::logical_children::Entity")] + LogicalChildren, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::LogicalChildren.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::logical_children::Relation::CascadesGroup.def() + } + fn via() -> Option { + Some( + super::logical_children::Relation::LogicalExpression + .def() + .rev(), + ) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-mvp/src/entities/mod.rs b/optd-mvp/src/entities/mod.rs new file mode 100644 index 0000000..701abe4 --- /dev/null +++ b/optd-mvp/src/entities/mod.rs @@ -0,0 +1,9 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +pub mod prelude; + +pub mod cascades_group; +pub mod logical_children; +pub mod logical_expression; +pub mod physical_children; +pub mod physical_expression; diff --git a/optd-mvp/src/entities/physical_children.rs b/optd-mvp/src/entities/physical_children.rs new file mode 100644 index 0000000..d8f9db0 --- /dev/null +++ b/optd-mvp/src/entities/physical_children.rs @@ -0,0 +1,46 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "physical_children")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub physical_expression_id: i32, + #[sea_orm(primary_key, auto_increment = false)] + pub group_id: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::cascades_group::Entity", + from = "Column::GroupId", + to = "super::cascades_group::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + CascadesGroup, + #[sea_orm( + belongs_to = "super::physical_expression::Entity", + from = "Column::PhysicalExpressionId", + to = "super::physical_expression::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + PhysicalExpression, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::CascadesGroup.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::PhysicalExpression.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-mvp/src/entities/physical_expression.rs b/optd-mvp/src/entities/physical_expression.rs new file mode 100644 index 0000000..2d9a2ae --- /dev/null +++ b/optd-mvp/src/entities/physical_expression.rs @@ -0,0 +1,49 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "physical_expression")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub group_id: i32, + pub fingerprint: i64, + pub kind: i16, + pub data: Json, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::cascades_group::Entity", + from = "Column::GroupId", + to = "super::cascades_group::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + CascadesGroup, + #[sea_orm(has_many = "super::physical_children::Entity")] + PhysicalChildren, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::PhysicalChildren.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::physical_children::Relation::CascadesGroup.def() + } + fn via() -> Option { + Some( + super::physical_children::Relation::PhysicalExpression + .def() + .rev(), + ) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-mvp/src/entities/prelude.rs b/optd-mvp/src/entities/prelude.rs new file mode 100644 index 0000000..0b8c910 --- /dev/null +++ b/optd-mvp/src/entities/prelude.rs @@ -0,0 +1,9 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +#![allow(unused_imports)] + +pub use super::cascades_group::Entity as CascadesGroup; +pub use super::logical_children::Entity as LogicalChildren; +pub use super::logical_expression::Entity as LogicalExpression; +pub use super::physical_children::Entity as PhysicalChildren; +pub use super::physical_expression::Entity as PhysicalExpression; diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_cascades_group.rs b/optd-mvp/src/migrator/memo/m20241127_000001_cascades_group.rs new file mode 100644 index 0000000..3a0e7d0 --- /dev/null +++ b/optd-mvp/src/migrator/memo/m20241127_000001_cascades_group.rs @@ -0,0 +1,123 @@ +//! An entity representing a group / equivalence class in the Cascades framework. +//! +//! Quoted from the Microsoft article _Extensible query optimizers in practice_: +//! +//! > In the memo, each class of equivalent expressions is called an equivalent class or a group, +//! > and all equivalent expressions within the class are called group expressions or simply +//! > expressions. +//! +//! A Cascades group is defined as a class of equivalent logical or physical expressions. The +//! Cascades framework uses these groups as a way of storing the best query sub-plans for use in the +//! dynamic programming search algorithm. +//! +//! For example, a Cascades group could be the set of expressions containing the logical expressions +//! `Join(A, B)` and `Join(B, A)`, as well as the physical expressions `HashJoin(A, B)` and +//! `NestedLoopJoin(B, A)`. +//! +//! # Columns +//! +//! Each group is assigned a monotonically-increasing (unique) ID. This ID will be important since +//! there are many foreign key references from other tables to `cascades_group`. +//! +//! We additionally store a `latest_winner` foreign key reference to a physical expression. See +//! the [section](#best-physical-plan-winner) below for more details. +//! +//! Finally, we store an `is_optimized` flag that is used for quickly determining the state of +//! optimization for this group during the dynamic programming search. +//! +//! # Entity Relationships +//! +//! ### Child Expressions (Logical and Physical) +//! +//! To retrieve all of a `cascades_group`'s equivalent expressions, you must query the +//! [`logical_expression`] or the [`physical_expression`] entities via their foreign keys to +//! `cascades_group`. The relationship between [`logical_expression`] and `cascades_group` is +//! many-to-one, and the exact same many-to-one relationship is held for [`physical_expression`] to +//! `cascades_group`. +//! +//! ### Parent Expressions (Logical and Physical) +//! +//! Additionally, each logical or physical expression can have any number of `cascades_group`s as +//! children, and a group can be a child of any expression. Thus, `cascades_group` additionally has +//! a many-to-many relationship with [`logical_expression`] and [`physical_expression`] via the +//! [`logical_children`] and [`physical_children`] entities. +//! +//! To reiterate, `cascades_group` has **both** a one-to-many **and** a many-to-many relationship +//! with both [`logical_expression`] and [`physical_expression`]. This is due to groups being both +//! parents and children of expressions. +//! +//! ### Best Physical Plan (Winner) +//! +//! The `cascades_group` entity also stores a `latest_winner` _nullable_ foreign key reference to +//! a physical expression. This represents the most recent best query plan we have computed. The +//! reason it is nullable is because we may not have come up with any best query plan yet. +//! +//! ### Logical Properties +//! +//! FIXME: Add a logical properties table. +//! +//! Lastly, each `cascades_group` record will have a set of logical properties store in the +//! `logical_property` entity, where there is an many-to-one relationship from +//! `logical_property` to `cascades_group`. Note that we do not store physical properties directly +//! on the `cascades_group`, but rather we store them for each [`physical_expression`] record. +//! +//! [`logical_expression`]: super::logical_expression +//! [`physical_expression`]: super::physical_expression +//! [`logical_children`]: super::logical_children +//! [`physical_children`]: super::physical_children +//! `logical_property`: super::logical_property + +use crate::migrator::memo::physical_expression::PhysicalExpression; +use sea_orm_migration::{prelude::*, schema::*}; + +#[derive(DeriveIden)] +pub enum CascadesGroup { + Table, + Id, + Winner, + Cost, + IsOptimized, + ParentId, +} + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(CascadesGroup::Table) + .if_not_exists() + .col(pk_auto(CascadesGroup::Id)) + .col(integer_null(CascadesGroup::Winner)) + .col(big_unsigned_null(CascadesGroup::Cost)) + .foreign_key( + ForeignKey::create() + .from(CascadesGroup::Table, CascadesGroup::Winner) + .to(PhysicalExpression::Table, PhysicalExpression::Id) + .on_delete(ForeignKeyAction::SetNull) + .on_update(ForeignKeyAction::Cascade), + ) + .col(boolean(CascadesGroup::IsOptimized)) + .col(integer_null(CascadesGroup::ParentId)) + .foreign_key( + ForeignKey::create() + .from(CascadesGroup::Table, CascadesGroup::ParentId) + .to(CascadesGroup::Table, CascadesGroup::Id) + .on_delete(ForeignKeyAction::SetNull) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(CascadesGroup::Table).to_owned()) + .await + } +} diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_logical_children.rs b/optd-mvp/src/migrator/memo/m20241127_000001_logical_children.rs new file mode 100644 index 0000000..d0835f4 --- /dev/null +++ b/optd-mvp/src/migrator/memo/m20241127_000001_logical_children.rs @@ -0,0 +1,65 @@ +//! An entity representing the [`cascades_group`] children of every [`logical_expression`]. +//! +//! Formally, this entity is a junction which allows us to represent a many-to-many relationship +//! between [`logical_expression`] and [`cascades_group`]. Expressions can have any number of child +//! groups, and every group can be a child of many different expressions, hence the many-to-many +//! relationship. +//! +//! See [`cascades_group`] for more details. +//! +//! [`cascades_group`]: super::cascades_group +//! [`logical_expression`]: super::logical_expression + +use crate::migrator::memo::{cascades_group::CascadesGroup, logical_expression::LogicalExpression}; +use sea_orm_migration::{prelude::*, schema::*}; + +#[derive(DeriveIden)] +pub enum LogicalChildren { + Table, + LogicalExpressionId, + GroupId, +} + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(LogicalChildren::Table) + .if_not_exists() + .col(integer(LogicalChildren::LogicalExpressionId)) + .col(integer(LogicalChildren::GroupId)) + .primary_key( + Index::create() + .col(LogicalChildren::LogicalExpressionId) + .col(LogicalChildren::GroupId), + ) + .foreign_key( + ForeignKey::create() + .from(LogicalChildren::Table, LogicalChildren::GroupId) + .to(LogicalExpression::Table, LogicalExpression::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .from(LogicalChildren::Table, LogicalChildren::GroupId) + .to(CascadesGroup::Table, CascadesGroup::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(LogicalChildren::Table).to_owned()) + .await + } +} diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_logical_expression.rs b/optd-mvp/src/migrator/memo/m20241127_000001_logical_expression.rs new file mode 100644 index 0000000..3682032 --- /dev/null +++ b/optd-mvp/src/migrator/memo/m20241127_000001_logical_expression.rs @@ -0,0 +1,84 @@ +//! An entity representing a logical plan expression in the Cascades framework. +//! +//! Quoted from the Microsoft article _Extensible query optimizers in practice_: +//! +//! > A logical expression is defined as a tree of logical operators, and corresponds to a +//! > relational algebraic expression. +//! +//! In the Cascades query optimization framework, the memo table stores equivalence classes of +//! expressions (see [`cascades_group`]). These equivalence classes, or "groups", store both +//! `logical_expression`s and [`physical_expression`]s. +//! +//! Optimization starts by "exploring" equivalent logical expressions within a group. For example, +//! the logical expressions `Join(A, B)` and `Join(B, A)` are contained in the same group. The +//! logical expressions are defined as a `Join` operator with the groups representing a scan of +//! table `A` and a scan of table `B` as its children. +//! +//! # Columns +//! +//! Each `logical_expression` has a unique primary key ID, but it holds little importance other than +//! helping distinguish between two different expressions. +//! +//! The more interesting column is the `fingerprint` column, in which we store a hashed fingerprint +//! value that can be used to efficiently check equality between two potentially equivalent logical +//! expressions (hash-consing). See ???FIXME??? for more information on expression fingerprints. +//! +//! Finally, since there are many different types of operators, we store a variant tag and a data +//! column as JSON to represent the semi-structured data fields of logical operators. +//! +//! # Entity Relationships +//! +//! The only relationship that `logical_expression` has is to [`cascades_group`]. It has **both** a +//! one-to-many **and** a many-to-many relationship with [`cascades_group`], and you can see more +//! details about this in the module-level documentation for [`cascades_group`]. +//! +//! [`cascades_group`]: super::cascades_group +//! [`physical_expression`]: super::physical_expression + +use crate::migrator::memo::cascades_group::CascadesGroup; +use sea_orm_migration::{prelude::*, schema::*}; + +#[derive(DeriveIden)] +pub enum LogicalExpression { + Table, + Id, + GroupId, + Fingerprint, + Kind, + Data, +} + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(LogicalExpression::Table) + .if_not_exists() + .col(pk_auto(LogicalExpression::Id)) + .col(integer(LogicalExpression::GroupId)) + .foreign_key( + ForeignKey::create() + .from(LogicalExpression::Table, LogicalExpression::GroupId) + .to(CascadesGroup::Table, CascadesGroup::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .col(big_unsigned(LogicalExpression::Fingerprint)) + .col(small_integer(LogicalExpression::Kind)) + .col(json(LogicalExpression::Data)) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(LogicalExpression::Table).to_owned()) + .await + } +} diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_physical_children.rs b/optd-mvp/src/migrator/memo/m20241127_000001_physical_children.rs new file mode 100644 index 0000000..3983f0c --- /dev/null +++ b/optd-mvp/src/migrator/memo/m20241127_000001_physical_children.rs @@ -0,0 +1,70 @@ +//! An entity representing the [`cascades_group`] children of every [`physical_expression`]. +//! +//! Formally, this entity is a junction which allows us to represent a many-to-many relationship +//! between [`physical_expression`] and [`cascades_group`]. Expressions can have any number of child +//! groups, and every group can be a child of many different expressions, hence the many-to-many +//! relationship. +//! +//! See [`cascades_group`] for more details. +//! +//! [`cascades_group`]: super::cascades_group +//! [`physical_expression`]: super::physical_expression + +use crate::migrator::memo::{ + cascades_group::CascadesGroup, physical_expression::PhysicalExpression, +}; +use sea_orm_migration::{prelude::*, schema::*}; + +#[derive(DeriveIden)] +pub enum PhysicalChildren { + Table, + PhysicalExpressionId, + GroupId, +} + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(PhysicalChildren::Table) + .if_not_exists() + .col(integer(PhysicalChildren::PhysicalExpressionId)) + .col(integer(PhysicalChildren::GroupId)) + .primary_key( + Index::create() + .col(PhysicalChildren::PhysicalExpressionId) + .col(PhysicalChildren::GroupId), + ) + .foreign_key( + ForeignKey::create() + .from( + PhysicalChildren::Table, + PhysicalChildren::PhysicalExpressionId, + ) + .to(PhysicalExpression::Table, PhysicalExpression::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .from(PhysicalChildren::Table, PhysicalChildren::GroupId) + .to(CascadesGroup::Table, CascadesGroup::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(PhysicalChildren::Table).to_owned()) + .await + } +} diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_physical_expression.rs b/optd-mvp/src/migrator/memo/m20241127_000001_physical_expression.rs new file mode 100644 index 0000000..7653112 --- /dev/null +++ b/optd-mvp/src/migrator/memo/m20241127_000001_physical_expression.rs @@ -0,0 +1,85 @@ +//! An entity representing a logical plan expression in the Cascades framework. +//! +//! Quoted from the Microsoft article _Extensible query optimizers in practice_: +//! +//! > A physical expression is a tree of physical operators, which is also referred to as the +//! > _physical plan_ or simply _plan_. +//! +//! In the Cascades query optimization framework, the memo table stores equivalence classes of +//! expressions (see [`cascades_group`]). These equivalence classes, or "groups", store both +//! [`logical_expression`]s and `physical_expression`s. +//! +//! Optimization starts by exploring equivalent logical expressions within a group, and then it +//! proceeds to implement / optimize those logical operators into physical operators. For example, +//! the logical expression `Join(A, B)` could be implemented into a `HashJoin(A, B)` or a +//! `NestedLoopJoin(A, B)`, and both of these new physical expressions would be contained in the +//! same group. +//! +//! # Columns +//! +//! Each `physical_expression` has a unique primary key ID, and other tables will store a foreign +//! key reference to a specific `physical_expression`s. +//! +//! The more interesting column is the `fingerprint` column, in which we store a hashed fingerprint +//! value that can be used to efficiently check equality between two potentially equivalent physical +//! expressions (hash-consing). See ???FIXME??? for more information on expression fingerprints. +//! +//! Finally, since there are many different types of operators, we store a variant tag and a data +//! column as JSON to represent the semi-structured data fields of logical operators. +//! +//! # Entity Relationships +//! +//! The only relationship that `physical_expression` has is to [`cascades_group`]. It has **both** a +//! one-to-many **and** a many-to-many relationship with [`cascades_group`], and you can see more +//! details about this in the module-level documentation for [`cascades_group`]. +//! +//! [`cascades_group`]: super::cascades_group +//! [`logical_expression`]: super::logical_expression + +use crate::migrator::memo::cascades_group::CascadesGroup; +use sea_orm_migration::{prelude::*, schema::*}; + +#[derive(DeriveIden)] +pub enum PhysicalExpression { + Table, + Id, + GroupId, + Fingerprint, + Kind, + Data, +} + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(PhysicalExpression::Table) + .if_not_exists() + .col(pk_auto(PhysicalExpression::Id)) + .col(integer(PhysicalExpression::GroupId)) + .foreign_key( + ForeignKey::create() + .from(PhysicalExpression::Table, PhysicalExpression::GroupId) + .to(CascadesGroup::Table, CascadesGroup::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .col(big_unsigned(PhysicalExpression::Fingerprint)) + .col(small_integer(PhysicalExpression::Kind)) + .col(json(PhysicalExpression::Data)) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(PhysicalExpression::Table).to_owned()) + .await + } +} diff --git a/optd-mvp/src/migrator/memo/mod.rs b/optd-mvp/src/migrator/memo/mod.rs new file mode 100644 index 0000000..8ed9390 --- /dev/null +++ b/optd-mvp/src/migrator/memo/mod.rs @@ -0,0 +1,14 @@ +//! Entities related to the memo table used for dynamic programming in the Cascades query +//! optimization framework. + +pub(crate) mod m20241127_000001_cascades_group; +pub(crate) mod m20241127_000001_logical_children; +pub(crate) mod m20241127_000001_logical_expression; +pub(crate) mod m20241127_000001_physical_children; +pub(crate) mod m20241127_000001_physical_expression; + +pub(crate) use m20241127_000001_cascades_group as cascades_group; +pub(crate) use m20241127_000001_logical_children as logical_children; +pub(crate) use m20241127_000001_logical_expression as logical_expression; +pub(crate) use m20241127_000001_physical_children as physical_children; +pub(crate) use m20241127_000001_physical_expression as physical_expression; diff --git a/optd-mvp/src/migrator/mod.rs b/optd-mvp/src/migrator/mod.rs new file mode 100644 index 0000000..179c406 --- /dev/null +++ b/optd-mvp/src/migrator/mod.rs @@ -0,0 +1,18 @@ +use sea_orm_migration::prelude::*; + +mod memo; + +pub struct Migrator; + +#[async_trait::async_trait] +impl MigratorTrait for Migrator { + fn migrations() -> Vec> { + vec![ + Box::new(memo::cascades_group::Migration), + Box::new(memo::logical_expression::Migration), + Box::new(memo::logical_children::Migration), + Box::new(memo::physical_expression::Migration), + Box::new(memo::physical_children::Migration), + ] + } +} From 0e54957d94269384ce009475b85a3f50281eb842 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Wed, 27 Nov 2024 19:21:48 -0500 Subject: [PATCH 04/10] add memo trait interface and persistent memo implementation This commit adds a first draft of a memo table trait and a persistent memo table implementation backed by SeaORM entities. --- optd-mvp/src/lib.rs | 21 +++ optd-mvp/src/memo/interface.rs | 146 +++++++++++++++++++ optd-mvp/src/memo/mod.rs | 9 ++ optd-mvp/src/memo/persistent.rs | 244 ++++++++++++++++++++++++++++++++ 4 files changed, 420 insertions(+) create mode 100644 optd-mvp/src/memo/interface.rs create mode 100644 optd-mvp/src/memo/mod.rs create mode 100644 optd-mvp/src/memo/persistent.rs diff --git a/optd-mvp/src/lib.rs b/optd-mvp/src/lib.rs index 5abd59f..c5185cd 100644 --- a/optd-mvp/src/lib.rs +++ b/optd-mvp/src/lib.rs @@ -1,16 +1,37 @@ use sea_orm::*; use sea_orm_migration::prelude::*; +use thiserror::Error; mod migrator; use migrator::Migrator; mod entities; +mod memo; +use memo::MemoError; + /// The filename of the SQLite database for migration. pub const DATABASE_FILENAME: &str = "sqlite.db"; /// The URL of the SQLite database for migration. pub const DATABASE_URL: &str = "sqlite:./sqlite.db?mode=rwc"; +/// An error type wrapping all the different kinds of error the optimizer might raise. +/// +/// TODO more docs. +#[derive(Error, Debug)] +pub enum OptimizerError { + #[error("SeaORM error")] + Database(#[from] sea_orm::error::DbErr), + #[error("Memo table logical error")] + Memo(#[from] MemoError), + #[error("unknown error")] + Unknown, +} + +/// Shorthand for a [`Result`] with an error type [`OptimizerError`]. +pub type OptimizerResult = Result; + +/// Applies all migrations. pub async fn migrate(db: &DatabaseConnection) -> Result<(), DbErr> { Migrator::refresh(db).await } diff --git a/optd-mvp/src/memo/interface.rs b/optd-mvp/src/memo/interface.rs new file mode 100644 index 0000000..a88740e --- /dev/null +++ b/optd-mvp/src/memo/interface.rs @@ -0,0 +1,146 @@ +use crate::OptimizerResult; +use thiserror::Error; + +#[derive(Error, Debug)] +/// The different kinds of errors that might occur while running operations on a memo table. +pub enum MemoError { + #[error("unknown group ID {0}")] + UnknownGroup(i32), + #[error("unknown logical expression ID {0}")] + UnknownLogicalExpression(i32), + #[error("unknown physical expression ID {0}")] + UnknownPhysicalExpression(i32), + #[error("invalid expression encountered")] + InvalidExpression, +} + +/// A trait representing an implementation of a memoization table. +/// +/// Note that we use [`trait_variant`] here in order to add bounds on every method. +/// See this [blog post]( +/// https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits.html#async-fn-in-public-traits) +/// for more information. +#[allow(dead_code)] +#[trait_variant::make(Send)] +pub trait Memo { + /// A type representing a group in the Cascades framework. + type Group; + /// A type representing a unique identifier for a group. + type GroupId; + /// A type representing a logical expression. + type LogicalExpression; + /// A type representing a unique identifier for a logical expression. + type LogicalExpressionId; + /// A type representing a physical expression. + type PhysicalExpression; + /// A type representing a unique identifier for a physical expression. + type PhysicalExpressionId; + + /// Retrieves a [`Self::Group`] given a [`Self::GroupId`]. + /// + /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. + async fn get_group(&self, group_id: Self::GroupId) -> OptimizerResult; + + /// Retrieves a [`Self::LogicalExpression`] given a [`Self::LogicalExpressionId`]. + /// + /// If the logical expression does not exist, returns a [`MemoError::UnknownLogicalExpression`] + /// error. + async fn get_logical_expression( + &self, + logical_expression_id: Self::LogicalExpressionId, + ) -> OptimizerResult; + + /// Retrieves a [`Self::PhysicalExpression`] given a [`Self::PhysicalExpressionId`]. + /// + /// If the physical expression does not exist, returns a + /// [`MemoError::UnknownPhysicalExpression`] error. + async fn get_physical_expression( + &self, + physical_expression_id: Self::PhysicalExpressionId, + ) -> OptimizerResult; + + /// Retrieves all of the logical expression "children" IDs of a group. + /// + /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. + async fn get_logical_children( + &self, + group_id: Self::GroupId, + ) -> OptimizerResult>; + + /// Retrieves all of the physical expression "children" IDs of a group. + /// + /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. + async fn get_physical_children( + &self, + group_id: Self::GroupId, + ) -> OptimizerResult>; + + /// Updates / replaces a group's best physical plan (winner). Optionally returns the previous + /// winner's physical expression ID. + /// + /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. + async fn update_group_winner( + &self, + group_id: Self::GroupId, + physical_expression_id: Self::PhysicalExpressionId, + ) -> OptimizerResult>; + + /// Adds a logical expression to an existing group via its [`Self::GroupId`]. This function + /// assumes that insertion of this expression would not create any duplicates. + /// + /// The caller is required to pass in a slice of `GroupId` that represent the child groups of + /// the input expression. + /// + /// The caller is also required to set the `group_id` field of the input `logical_expression` + /// to be equal to `group_id`, otherwise this function will return a + /// [`MemoError::InvalidExpression`] error. + /// + /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. + async fn add_logical_expression_to_group( + &self, + group_id: Self::GroupId, + logical_expression: Self::LogicalExpression, + children: &[Self::GroupId], + ) -> OptimizerResult<()>; + + /// Adds a physical expression to an existing group via its [`Self::GroupId`]. This function + /// assumes that insertion of this expression would not create any duplicates. + /// + /// The caller is required to pass in a slice of `GroupId` that represent the child groups of + /// the input expression. + /// + /// The caller is also required to set the `group_id` field of the input `physical_expression` + /// to be equal to `group_id`, otherwise this function will return a + /// [`MemoError::InvalidExpression`] error. + /// + /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. + async fn add_physical_expression_to_group( + &self, + group_id: Self::GroupId, + physical_expression: Self::PhysicalExpression, + children: &[Self::GroupId], + ) -> OptimizerResult<()>; + + /// Adds a new logical expression into the memo table, creating a new group if the expression + /// does not already exist. + /// + /// The caller is required to pass in a slice of `GroupId` that represent the child groups of + /// the input expression. + /// + /// The [`Self::LogicalExpression`] type should have some sort of mechanism for checking if + /// the expression has been seen before, and if it has already been created, then the parent + /// group ID should also be retrievable. + /// + /// If the expression already exists, then this function will return the [`Self::GroupId`] of + /// the parent group and the corresponding (already existing) [`Self::LogicalExpressionId`]. It + /// will also completely ignore the group ID field of the input expression as well as ignore the + /// input slice of child groups. + /// + /// If the expression does not exist, this function will create a new group and a new + /// expression, returning brand new IDs for both. + async fn add_logical_expression( + &self, + expression: Self::LogicalExpression, + children: &[Self::LogicalExpressionId], + ) -> OptimizerResult<(Self::GroupId, Self::LogicalExpressionId)>; +} diff --git a/optd-mvp/src/memo/mod.rs b/optd-mvp/src/memo/mod.rs new file mode 100644 index 0000000..5253352 --- /dev/null +++ b/optd-mvp/src/memo/mod.rs @@ -0,0 +1,9 @@ +//! This module contains items related to the memo table, which is key to the Cascades query +//! optimization framework. +//! +//! TODO more docs. + +mod persistent; + +mod interface; +pub use interface::{Memo, MemoError}; diff --git a/optd-mvp/src/memo/persistent.rs b/optd-mvp/src/memo/persistent.rs new file mode 100644 index 0000000..445ee6c --- /dev/null +++ b/optd-mvp/src/memo/persistent.rs @@ -0,0 +1,244 @@ +use crate::{ + entities::{prelude::*, *}, + memo::{Memo, MemoError}, + OptimizerResult, DATABASE_URL, +}; +use sea_orm::*; + +/// A persistent memo table, backed by a database on disk. +/// +/// TODO more docs. +pub struct PersistentMemo { + /// This `PersistentMemo` is reliant on the SeaORM [`DatabaseConnection`] that stores all of the + /// objects needed for query optimization. + db: DatabaseConnection, +} + +impl PersistentMemo { + /// TODO remove dead code and write docs. + #[allow(dead_code)] + pub async fn new() -> Self { + Self { + db: Database::connect(DATABASE_URL).await.unwrap(), + } + } +} + +impl Memo for PersistentMemo { + type Group = cascades_group::Model; + type GroupId = i32; + type LogicalExpression = logical_expression::Model; + type LogicalExpressionId = i32; + type PhysicalExpression = physical_expression::Model; + type PhysicalExpressionId = i32; + + async fn get_group(&self, group_id: Self::GroupId) -> OptimizerResult { + Ok(CascadesGroup::find_by_id(group_id) + .one(&self.db) + .await? + .ok_or(MemoError::UnknownGroup(group_id))?) + } + + async fn get_logical_expression( + &self, + logical_expression_id: Self::LogicalExpressionId, + ) -> OptimizerResult { + Ok(LogicalExpression::find_by_id(logical_expression_id) + .one(&self.db) + .await? + .ok_or(MemoError::UnknownLogicalExpression(logical_expression_id))?) + } + + async fn get_physical_expression( + &self, + physical_expression_id: Self::PhysicalExpressionId, + ) -> OptimizerResult { + Ok(PhysicalExpression::find_by_id(physical_expression_id) + .one(&self.db) + .await? + .ok_or(MemoError::UnknownPhysicalExpression(physical_expression_id))?) + } + + async fn get_logical_children( + &self, + group_id: Self::GroupId, + ) -> OptimizerResult> { + // First retrieve the group record, and then find all related logical expressions. + Ok(self + .get_group(group_id) + .await? + .find_related(LogicalChildren) + .all(&self.db) + .await? + .into_iter() + .map(|m| m.logical_expression_id) + .collect()) + } + + async fn get_physical_children( + &self, + group_id: Self::GroupId, + ) -> OptimizerResult> { + // First retrieve the group record, and then find all related physical expressions. + Ok(self + .get_group(group_id) + .await? + .find_related(PhysicalChildren) + .all(&self.db) + .await? + .into_iter() + .map(|m| m.physical_expression_id) + .collect()) + } + + /// FIXME: In the future, this should first check that we aren't overwriting a winner that was + /// updated from another thread. + async fn update_group_winner( + &self, + group_id: Self::GroupId, + physical_expression_id: Self::PhysicalExpressionId, + ) -> OptimizerResult> { + // First retrieve the group record, and then use an `ActiveModel` to update it. + let mut group = self.get_group(group_id).await?.into_active_model(); + let old_id = group.winner; + + group.winner = Set(Some(physical_expression_id)); + group.update(&self.db).await?; + + // The old value must be set (`None` still means it has been set). + let old = old_id.unwrap(); + Ok(old) + } + + async fn add_logical_expression_to_group( + &self, + group_id: Self::GroupId, + logical_expression: Self::LogicalExpression, + children: &[Self::GroupId], + ) -> OptimizerResult<()> { + if logical_expression.group_id != group_id { + Err(MemoError::InvalidExpression)? + } + + // Check if the group actually exists. + let _ = self.get_group(group_id).await?; + + // Insert the child groups of the expression into the junction / children table. + if !children.is_empty() { + LogicalChildren::insert_many(children.iter().copied().map(|group_id| { + logical_children::ActiveModel { + logical_expression_id: Set(logical_expression.id), + group_id: Set(group_id), + } + })) + .exec(&self.db) + .await?; + } + + // Insert the expression. + let _ = logical_expression + .into_active_model() + .insert(&self.db) + .await?; + + Ok(()) + } + + async fn add_physical_expression_to_group( + &self, + group_id: Self::GroupId, + physical_expression: Self::PhysicalExpression, + children: &[Self::GroupId], + ) -> OptimizerResult<()> { + if physical_expression.group_id != group_id { + Err(MemoError::InvalidExpression)? + } + + // Check if the group actually exists. + let _ = self.get_group(group_id).await?; + + // Insert the child groups of the expression into the junction / children table. + if !children.is_empty() { + PhysicalChildren::insert_many(children.iter().copied().map(|group_id| { + physical_children::ActiveModel { + physical_expression_id: Set(physical_expression.id), + group_id: Set(group_id), + } + })) + .exec(&self.db) + .await?; + } + + // Insert the expression. + let _ = physical_expression + .into_active_model() + .insert(&self.db) + .await?; + + Ok(()) + } + + async fn add_logical_expression( + &self, + logical_expression: Self::LogicalExpression, + children: &[Self::GroupId], + ) -> OptimizerResult<(Self::GroupId, Self::LogicalExpressionId)> { + // Lookup all expressions that have the same fingerprint. There may be false positives, but + // we will check for those later. + let fingerprint = logical_expression.fingerprint; + let potential_matches = LogicalExpression::find() + .filter(logical_expression::Column::Fingerprint.eq(fingerprint)) + .all(&self.db) + .await?; + + // Of the expressions that have the same fingerprint, check if there already exists an + // expression that is exactly identical to the input expression. + let mut matches: Vec<_> = potential_matches + .into_iter() + .filter(|expr| expr == &logical_expression) + .collect(); + assert!( + matches.len() <= 1, + "there cannot be more than 1 exact logical expression match" + ); + + // The expression already exists, so return its data. + if !matches.is_empty() { + let existing_expression = matches + .pop() + .expect("we just checked that an element exists"); + + return Ok((existing_expression.group_id, existing_expression.id)); + } + + // The expression does not exist yet, so we need to create a new group and new expression. + let group = cascades_group::ActiveModel { + winner: Set(None), + is_optimized: Set(false), + ..Default::default() + }; + + // Create a new group. + let res = cascades_group::Entity::insert(group).exec(&self.db).await?; + + // Insert the input expression with the correct `group_id`. + let mut new_expr = logical_expression.into_active_model(); + new_expr.group_id = Set(res.last_insert_id); + new_expr.id = NotSet; + let new_expr = new_expr.insert(&self.db).await?; + + // Insert the child groups of the expression into the junction / children table. + if !children.is_empty() { + LogicalChildren::insert_many(children.iter().copied().map(|group_id| { + logical_children::ActiveModel { + logical_expression_id: Set(new_expr.id), + group_id: Set(group_id), + } + })) + .exec(&self.db) + .await?; + } + + Ok((new_expr.group_id, new_expr.id)) + } +} From 285649686f56507c5a830f57f5ea7ce0fe4813c1 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Thu, 28 Nov 2024 14:43:32 -0500 Subject: [PATCH 05/10] add expression representation and refactor memo This commit adds the `src/expression` module which contains a very simple representation of Cascades expressions. The `Memo` trait interface and implemenation has also changed, where it now correctly detects exact match duplicates, and it does not track fingerprints for physical expressions (only logical). TODO: Add more tests. TODO: Figure out how to test in CI. --- .gitignore | 2 +- optd-mvp/src/entities/fingerprint.rs | 33 ++++ optd-mvp/src/entities/logical_expression.rs | 9 +- optd-mvp/src/entities/mod.rs | 1 + optd-mvp/src/entities/physical_expression.rs | 1 - optd-mvp/src/entities/prelude.rs | 3 +- optd-mvp/src/expression/logical_expression.rs | 117 +++++++++++++ optd-mvp/src/expression/mod.rs | 62 +++++++ .../src/expression/physical_expression.rs | 115 +++++++++++++ optd-mvp/src/lib.rs | 14 ++ optd-mvp/src/memo/interface.rs | 58 +++++-- .../implementation.rs} | 162 ++++++++++-------- optd-mvp/src/memo/persistent/mod.rs | 66 +++++++ optd-mvp/src/memo/persistent/tests.rs | 36 ++++ .../memo/m20241127_000001_fingerprint.rs | 49 ++++++ .../m20241127_000001_logical_expression.rs | 14 +- .../m20241127_000001_physical_expression.rs | 9 +- optd-mvp/src/migrator/memo/mod.rs | 2 + optd-mvp/src/migrator/mod.rs | 1 + 19 files changed, 655 insertions(+), 99 deletions(-) create mode 100644 optd-mvp/src/entities/fingerprint.rs create mode 100644 optd-mvp/src/expression/logical_expression.rs create mode 100644 optd-mvp/src/expression/mod.rs create mode 100644 optd-mvp/src/expression/physical_expression.rs rename optd-mvp/src/memo/{persistent.rs => persistent/implementation.rs} (68%) create mode 100644 optd-mvp/src/memo/persistent/mod.rs create mode 100644 optd-mvp/src/memo/persistent/tests.rs create mode 100644 optd-mvp/src/migrator/memo/m20241127_000001_fingerprint.rs diff --git a/.gitignore b/.gitignore index 861fce8..86281ff 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,4 @@ target/ # We will check in all code-generated entity files, as newer versions of `sea-orm-cli` might # conflict with previous versions. -# **/entities \ No newline at end of file +# **/entities diff --git a/optd-mvp/src/entities/fingerprint.rs b/optd-mvp/src/entities/fingerprint.rs new file mode 100644 index 0000000..2ab6a7f --- /dev/null +++ b/optd-mvp/src/entities/fingerprint.rs @@ -0,0 +1,33 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "fingerprint")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub logical_expression_id: i32, + pub kind: i16, + pub hash: i64, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::logical_expression::Entity", + from = "Column::LogicalExpressionId", + to = "super::logical_expression::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + LogicalExpression, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::LogicalExpression.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/optd-mvp/src/entities/logical_expression.rs b/optd-mvp/src/entities/logical_expression.rs index 1e85d1d..4c257f3 100644 --- a/optd-mvp/src/entities/logical_expression.rs +++ b/optd-mvp/src/entities/logical_expression.rs @@ -8,7 +8,6 @@ pub struct Model { #[sea_orm(primary_key)] pub id: i32, pub group_id: i32, - pub fingerprint: i64, pub kind: i16, pub data: Json, } @@ -23,10 +22,18 @@ pub enum Relation { on_delete = "Cascade" )] CascadesGroup, + #[sea_orm(has_many = "super::fingerprint::Entity")] + Fingerprint, #[sea_orm(has_many = "super::logical_children::Entity")] LogicalChildren, } +impl Related for Entity { + fn to() -> RelationDef { + Relation::Fingerprint.def() + } +} + impl Related for Entity { fn to() -> RelationDef { Relation::LogicalChildren.def() diff --git a/optd-mvp/src/entities/mod.rs b/optd-mvp/src/entities/mod.rs index 701abe4..77d6b2c 100644 --- a/optd-mvp/src/entities/mod.rs +++ b/optd-mvp/src/entities/mod.rs @@ -3,6 +3,7 @@ pub mod prelude; pub mod cascades_group; +pub mod fingerprint; pub mod logical_children; pub mod logical_expression; pub mod physical_children; diff --git a/optd-mvp/src/entities/physical_expression.rs b/optd-mvp/src/entities/physical_expression.rs index 2d9a2ae..482227a 100644 --- a/optd-mvp/src/entities/physical_expression.rs +++ b/optd-mvp/src/entities/physical_expression.rs @@ -8,7 +8,6 @@ pub struct Model { #[sea_orm(primary_key)] pub id: i32, pub group_id: i32, - pub fingerprint: i64, pub kind: i16, pub data: Json, } diff --git a/optd-mvp/src/entities/prelude.rs b/optd-mvp/src/entities/prelude.rs index 0b8c910..5619363 100644 --- a/optd-mvp/src/entities/prelude.rs +++ b/optd-mvp/src/entities/prelude.rs @@ -1,8 +1,7 @@ //! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 -#![allow(unused_imports)] - pub use super::cascades_group::Entity as CascadesGroup; +pub use super::fingerprint::Entity as Fingerprint; pub use super::logical_children::Entity as LogicalChildren; pub use super::logical_expression::Entity as LogicalExpression; pub use super::physical_children::Entity as PhysicalChildren; diff --git a/optd-mvp/src/expression/logical_expression.rs b/optd-mvp/src/expression/logical_expression.rs new file mode 100644 index 0000000..c87b055 --- /dev/null +++ b/optd-mvp/src/expression/logical_expression.rs @@ -0,0 +1,117 @@ +//! Definition of logical expressions / relations in the Cascades query optimization framework. +//! +//! FIXME: All fields are placeholders, and group IDs are just represented as i32 for now. +//! FIXME: Representation needs to know how to "rewrite" child group IDs to whatever a fingerprint +//! will need. +//! +//! TODO figure out if each relation should be in a different submodule. +//! TODO This entire file is a WIP. + +use crate::entities::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug)] +pub enum LogicalExpression { + Scan(Scan), + Filter(Filter), + Join(Join), +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Scan { + table_schema: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Filter { + child: i32, + expression: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Join { + left: i32, + right: i32, + expression: String, +} + +/// TODO Use a macro instead. +impl From for LogicalExpression { + fn from(value: logical_expression::Model) -> Self { + match value.kind { + 0 => Self::Scan( + serde_json::from_value(value.data) + .expect("unable to deserialize data into a logical `Scan`"), + ), + 1 => Self::Filter( + serde_json::from_value(value.data) + .expect("Unable to deserialize data into a logical `Filter`"), + ), + 2 => Self::Join( + serde_json::from_value(value.data) + .expect("Unable to deserialize data into a logical `Join`"), + ), + _ => panic!(), + } + } +} + +/// TODO Use a macro instead. +impl From for logical_expression::Model { + fn from(value: LogicalExpression) -> logical_expression::Model { + fn create_logical_expression( + kind: i16, + data: serde_json::Value, + ) -> logical_expression::Model { + logical_expression::Model { + id: -1, + group_id: -1, + kind, + data, + } + } + + match value { + LogicalExpression::Scan(scan) => create_logical_expression( + 0, + serde_json::to_value(scan).expect("unable to serialize logical `Scan`"), + ), + LogicalExpression::Filter(filter) => create_logical_expression( + 1, + serde_json::to_value(filter).expect("unable to serialize logical `Filter`"), + ), + LogicalExpression::Join(join) => create_logical_expression( + 2, + serde_json::to_value(join).expect("unable to serialize logical `Join`"), + ), + } + } +} + +#[cfg(test)] +pub use build::*; + +#[cfg(test)] +mod build { + use super::*; + use crate::expression::Expression; + + pub fn scan(table_schema: String) -> Expression { + Expression::Logical(LogicalExpression::Scan(Scan { table_schema })) + } + + pub fn filter(child_group: i32, expression: String) -> Expression { + Expression::Logical(LogicalExpression::Filter(Filter { + child: child_group, + expression, + })) + } + + pub fn join(left_group: i32, right_group: i32, expression: String) -> Expression { + Expression::Logical(LogicalExpression::Join(Join { + left: left_group, + right: right_group, + expression, + })) + } +} diff --git a/optd-mvp/src/expression/mod.rs b/optd-mvp/src/expression/mod.rs new file mode 100644 index 0000000..459e13b --- /dev/null +++ b/optd-mvp/src/expression/mod.rs @@ -0,0 +1,62 @@ +//! In-memory representation of Cascades logical and physical expression / operators / relations. +//! +//! TODO more docs. + +mod logical_expression; +pub use logical_expression::*; + +mod physical_expression; +pub use physical_expression::*; + +/// The representation of a Cascades expression. +/// +/// TODO more docs. +#[derive(Clone, Debug)] +pub enum Expression { + Logical(LogicalExpression), + Physical(PhysicalExpression), +} + +/// Converts the database / JSON representation of a logical expression into an in-memory one. +impl From for Expression { + fn from(value: crate::entities::logical_expression::Model) -> Self { + Self::Logical(value.into()) + } +} + +/// Converts the in-memory representation of a logical expression into the database / JSON version. +/// +/// # Panics +/// +/// This will panic if the [`Expression`] is [`Expression::Physical`]. +impl From for crate::entities::logical_expression::Model { + fn from(value: Expression) -> Self { + let Expression::Logical(expr) = value else { + panic!("Attempted to convert an in-memory physical expression into a logical database / JSON expression"); + }; + + expr.into() + } +} + +/// Converts the database / JSON representation of a physical expression into an in-memory one. +impl From for Expression { + fn from(value: crate::entities::physical_expression::Model) -> Self { + Self::Physical(value.into()) + } +} + +/// Converts the in-memory representation of a physical expression into the database / JSON version. +/// +/// # Panics +/// +/// This will panic if the [`Expression`] is [`Expression::Physical`]. +impl From for crate::entities::physical_expression::Model { + fn from(value: Expression) -> Self { + let Expression::Physical(expr) = value else { + panic!("Attempted to convert an in-memory logical expression into a physical database / JSON expression"); + }; + + expr.into() + } +} diff --git a/optd-mvp/src/expression/physical_expression.rs b/optd-mvp/src/expression/physical_expression.rs new file mode 100644 index 0000000..6552a96 --- /dev/null +++ b/optd-mvp/src/expression/physical_expression.rs @@ -0,0 +1,115 @@ +//! Definition of physical expressions / operators in the Cascades query optimization framework. +//! +//! FIXME: All fields are placeholders, and group IDs are just represented as i32 for now. +//! +//! TODO figure out if each operator should be in a different submodule. +//! TODO This entire file is a WIP. + +use crate::entities::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug)] +pub enum PhysicalExpression { + TableScan(TableScan), + Filter(PhysicalFilter), + HashJoin(HashJoin), +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct TableScan { + table_schema: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct PhysicalFilter { + child: i32, + expression: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct HashJoin { + left: i32, + right: i32, + expression: String, +} + +/// TODO Use a macro instead. +impl From for PhysicalExpression { + fn from(value: physical_expression::Model) -> Self { + match value.kind { + 0 => Self::TableScan( + serde_json::from_value(value.data) + .expect("unable to deserialize data into a physical `TableScan`"), + ), + 1 => Self::Filter( + serde_json::from_value(value.data) + .expect("Unable to deserialize data into a physical `Filter`"), + ), + 2 => Self::HashJoin( + serde_json::from_value(value.data) + .expect("Unable to deserialize data into a physical `HashJoin`"), + ), + _ => panic!(), + } + } +} + +/// TODO Use a macro instead. +impl From for physical_expression::Model { + fn from(value: PhysicalExpression) -> physical_expression::Model { + fn create_physical_expression( + kind: i16, + data: serde_json::Value, + ) -> physical_expression::Model { + physical_expression::Model { + id: -1, + group_id: -1, + kind, + data, + } + } + + match value { + PhysicalExpression::TableScan(scan) => create_physical_expression( + 0, + serde_json::to_value(scan).expect("unable to serialize physical `TableScan`"), + ), + PhysicalExpression::Filter(filter) => create_physical_expression( + 1, + serde_json::to_value(filter).expect("unable to serialize physical `Filter`"), + ), + PhysicalExpression::HashJoin(join) => create_physical_expression( + 2, + serde_json::to_value(join).expect("unable to serialize physical `HashJoin`"), + ), + } + } +} + +#[cfg(test)] +pub use build::*; + +#[cfg(test)] +mod build { + use super::*; + use crate::expression::Expression; + + pub fn table_scan(table_schema: String) -> Expression { + Expression::Physical(PhysicalExpression::TableScan(TableScan { table_schema })) + } + + pub fn filter(child_group: i32, expression: String) -> Expression { + Expression::Physical(PhysicalExpression::Filter(PhysicalFilter { + child: child_group, + expression, + })) + } + + pub fn hash_join(left_group: i32, right_group: i32, expression: String) -> Expression { + Expression::Physical(PhysicalExpression::HashJoin(HashJoin { + left: left_group, + right: right_group, + expression, + })) + } +} diff --git a/optd-mvp/src/lib.rs b/optd-mvp/src/lib.rs index c5185cd..98c5f11 100644 --- a/optd-mvp/src/lib.rs +++ b/optd-mvp/src/lib.rs @@ -10,6 +10,8 @@ mod entities; mod memo; use memo::MemoError; +mod expression; + /// The filename of the SQLite database for migration. pub const DATABASE_FILENAME: &str = "sqlite.db"; /// The URL of the SQLite database for migration. @@ -35,3 +37,15 @@ pub type OptimizerResult = Result; pub async fn migrate(db: &DatabaseConnection) -> Result<(), DbErr> { Migrator::refresh(db).await } + +/// Helper function for hashing expression data. +/// +/// TODO remove this. +fn hash_expression(kind: i16, data: &serde_json::Value) -> i64 { + use std::hash::{DefaultHasher, Hash, Hasher}; + + let mut hasher = DefaultHasher::new(); + kind.hash(&mut hasher); + data.hash(&mut hasher); + hasher.finish() as i64 +} diff --git a/optd-mvp/src/memo/interface.rs b/optd-mvp/src/memo/interface.rs index a88740e..cb6c76d 100644 --- a/optd-mvp/src/memo/interface.rs +++ b/optd-mvp/src/memo/interface.rs @@ -1,8 +1,11 @@ +//! This module defines the [`Memo`] trait, which defines shared behavior of all memo table that can +//! be used for query optimization in the Cascades framework. + use crate::OptimizerResult; use thiserror::Error; -#[derive(Error, Debug)] /// The different kinds of errors that might occur while running operations on a memo table. +#[derive(Error, Debug)] pub enum MemoError { #[error("unknown group ID {0}")] UnknownGroup(i32), @@ -20,6 +23,8 @@ pub enum MemoError { /// See this [blog post]( /// https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits.html#async-fn-in-public-traits) /// for more information. +/// +/// TODO remove dead code. #[allow(dead_code)] #[trait_variant::make(Send)] pub trait Memo { @@ -75,6 +80,18 @@ pub trait Memo { group_id: Self::GroupId, ) -> OptimizerResult>; + /// Checks if a given logical expression is a duplicate / already exists in the memo table. + /// + /// In order to prevent a large amount of duplicate work, the memo table must support duplicate + /// expression detection. + /// + /// Returns `Some(expression_id)` if the memo table detects that the expression already exists, + /// and `None` otherwise. + async fn is_duplicate_logical_expression( + &self, + logical_expression: &Self::LogicalExpression, + ) -> OptimizerResult>; + /// Updates / replaces a group's best physical plan (winner). Optionally returns the previous /// winner's physical expression ID. /// @@ -85,41 +102,49 @@ pub trait Memo { physical_expression_id: Self::PhysicalExpressionId, ) -> OptimizerResult>; - /// Adds a logical expression to an existing group via its [`Self::GroupId`]. This function - /// assumes that insertion of this expression would not create any duplicates. + /// Adds a physical expression to an existing group via its [`Self::GroupId`]. /// /// The caller is required to pass in a slice of `GroupId` that represent the child groups of /// the input expression. /// - /// The caller is also required to set the `group_id` field of the input `logical_expression` + /// The caller is also required to set the `group_id` field of the input `physical_expression` /// to be equal to `group_id`, otherwise this function will return a /// [`MemoError::InvalidExpression`] error. /// /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. - async fn add_logical_expression_to_group( + /// + /// On successful insertion, returns the ID of the physical expression. + async fn add_physical_expression_to_group( &self, group_id: Self::GroupId, - logical_expression: Self::LogicalExpression, + physical_expression: Self::PhysicalExpression, children: &[Self::GroupId], - ) -> OptimizerResult<()>; + ) -> OptimizerResult; - /// Adds a physical expression to an existing group via its [`Self::GroupId`]. This function - /// assumes that insertion of this expression would not create any duplicates. + /// Adds a logical expression to an existing group via its [`Self::GroupId`]. /// /// The caller is required to pass in a slice of `GroupId` that represent the child groups of /// the input expression. /// - /// The caller is also required to set the `group_id` field of the input `physical_expression` + /// The caller is also required to set the `group_id` field of the input `logical_expression` /// to be equal to `group_id`, otherwise this function will return a /// [`MemoError::InvalidExpression`] error. /// /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. - async fn add_physical_expression_to_group( + /// + /// If the memo table detects that the input logical expression is a duplicate expression, it + /// will **not** insert the expression into the memo table. Instead, it will return an + /// `Ok(Err(expression_id))`, which is a unique identifier of the expression that the input is a + /// duplicate of. The caller can use this ID to retrieve the group the original belongs to. + /// + /// If the memo table detects that the input is unique, it will insert the expression into the + /// input group and return an `Ok(Ok(expression_id))`. + async fn add_logical_expression_to_group( &self, group_id: Self::GroupId, - physical_expression: Self::PhysicalExpression, + logical_expression: Self::LogicalExpression, children: &[Self::GroupId], - ) -> OptimizerResult<()>; + ) -> OptimizerResult>; /// Adds a new logical expression into the memo table, creating a new group if the expression /// does not already exist. @@ -142,5 +167,10 @@ pub trait Memo { &self, expression: Self::LogicalExpression, children: &[Self::LogicalExpressionId], - ) -> OptimizerResult<(Self::GroupId, Self::LogicalExpressionId)>; + ) -> OptimizerResult< + Result< + (Self::GroupId, Self::LogicalExpressionId), + (Self::GroupId, Self::LogicalExpressionId), + >, + >; } diff --git a/optd-mvp/src/memo/persistent.rs b/optd-mvp/src/memo/persistent/implementation.rs similarity index 68% rename from optd-mvp/src/memo/persistent.rs rename to optd-mvp/src/memo/persistent/implementation.rs index 445ee6c..4c06c4e 100644 --- a/optd-mvp/src/memo/persistent.rs +++ b/optd-mvp/src/memo/persistent/implementation.rs @@ -1,28 +1,11 @@ +//! This module contains the implementation of the [`Memo`] trait for [`PersistentMemo`]. + +use super::*; use crate::{ - entities::{prelude::*, *}, + hash_expression, memo::{Memo, MemoError}, - OptimizerResult, DATABASE_URL, + OptimizerResult, }; -use sea_orm::*; - -/// A persistent memo table, backed by a database on disk. -/// -/// TODO more docs. -pub struct PersistentMemo { - /// This `PersistentMemo` is reliant on the SeaORM [`DatabaseConnection`] that stores all of the - /// objects needed for query optimization. - db: DatabaseConnection, -} - -impl PersistentMemo { - /// TODO remove dead code and write docs. - #[allow(dead_code)] - pub async fn new() -> Self { - Self { - db: Database::connect(DATABASE_URL).await.unwrap(), - } - } -} impl Memo for PersistentMemo { type Group = cascades_group::Model; @@ -91,6 +74,41 @@ impl Memo for PersistentMemo { .collect()) } + /// FIXME Check that all of the children are root groups? + async fn is_duplicate_logical_expression( + &self, + logical_expression: &Self::LogicalExpression, + ) -> OptimizerResult> { + // Lookup all expressions that have the same fingerprint and kind. There may be false + // positives, but we will check for those next. + let kind = logical_expression.kind; + let fingerprint = hash_expression(kind, &logical_expression.data); + + let potential_matches = Fingerprint::find() + .filter(fingerprint::Column::Hash.eq(fingerprint)) + .filter(fingerprint::Column::Kind.eq(kind)) + .all(&self.db) + .await?; + + if potential_matches.is_empty() { + return Ok(None); + } + + let mut match_id = None; + for potential_match in potential_matches { + let expr_id = potential_match.logical_expression_id; + let expr = self.get_logical_expression(expr_id).await?; + + if expr.data == logical_expression.data { + // There should be at most one duplicate expression. + match_id = Some(expr_id); + break; + } + } + + Ok(match_id) + } + /// FIXME: In the future, this should first check that we aren't overwriting a winner that was /// updated from another thread. async fn update_group_winner( @@ -110,13 +128,13 @@ impl Memo for PersistentMemo { Ok(old) } - async fn add_logical_expression_to_group( + async fn add_physical_expression_to_group( &self, group_id: Self::GroupId, - logical_expression: Self::LogicalExpression, + physical_expression: Self::PhysicalExpression, children: &[Self::GroupId], - ) -> OptimizerResult<()> { - if logical_expression.group_id != group_id { + ) -> OptimizerResult { + if physical_expression.group_id != group_id { Err(MemoError::InvalidExpression)? } @@ -125,9 +143,9 @@ impl Memo for PersistentMemo { // Insert the child groups of the expression into the junction / children table. if !children.is_empty() { - LogicalChildren::insert_many(children.iter().copied().map(|group_id| { - logical_children::ActiveModel { - logical_expression_id: Set(logical_expression.id), + PhysicalChildren::insert_many(children.iter().copied().map(|group_id| { + physical_children::ActiveModel { + physical_expression_id: Set(physical_expression.id), group_id: Set(group_id), } })) @@ -136,32 +154,41 @@ impl Memo for PersistentMemo { } // Insert the expression. - let _ = logical_expression + let res = physical_expression .into_active_model() .insert(&self.db) .await?; - Ok(()) + Ok(res.id) } - async fn add_physical_expression_to_group( + /// FIXME Check that all of the children are reduced groups? + async fn add_logical_expression_to_group( &self, group_id: Self::GroupId, - physical_expression: Self::PhysicalExpression, + logical_expression: Self::LogicalExpression, children: &[Self::GroupId], - ) -> OptimizerResult<()> { - if physical_expression.group_id != group_id { + ) -> OptimizerResult> { + if logical_expression.group_id != group_id { Err(MemoError::InvalidExpression)? } + // Check if the expression already exists in the memo table. + if let Some(existing_id) = self + .is_duplicate_logical_expression(&logical_expression) + .await? + { + return Ok(Err(existing_id)); + } + // Check if the group actually exists. let _ = self.get_group(group_id).await?; // Insert the child groups of the expression into the junction / children table. if !children.is_empty() { - PhysicalChildren::insert_many(children.iter().copied().map(|group_id| { - physical_children::ActiveModel { - physical_expression_id: Set(physical_expression.id), + LogicalChildren::insert_many(children.iter().copied().map(|group_id| { + logical_children::ActiveModel { + logical_expression_id: Set(logical_expression.id), group_id: Set(group_id), } })) @@ -170,45 +197,32 @@ impl Memo for PersistentMemo { } // Insert the expression. - let _ = physical_expression + let res = logical_expression .into_active_model() .insert(&self.db) .await?; - Ok(()) + Ok(Ok(res.id)) } + /// FIXME Check that all of the children are reduced groups? async fn add_logical_expression( &self, logical_expression: Self::LogicalExpression, children: &[Self::GroupId], - ) -> OptimizerResult<(Self::GroupId, Self::LogicalExpressionId)> { - // Lookup all expressions that have the same fingerprint. There may be false positives, but - // we will check for those later. - let fingerprint = logical_expression.fingerprint; - let potential_matches = LogicalExpression::find() - .filter(logical_expression::Column::Fingerprint.eq(fingerprint)) - .all(&self.db) - .await?; - - // Of the expressions that have the same fingerprint, check if there already exists an - // expression that is exactly identical to the input expression. - let mut matches: Vec<_> = potential_matches - .into_iter() - .filter(|expr| expr == &logical_expression) - .collect(); - assert!( - matches.len() <= 1, - "there cannot be more than 1 exact logical expression match" - ); - - // The expression already exists, so return its data. - if !matches.is_empty() { - let existing_expression = matches - .pop() - .expect("we just checked that an element exists"); - - return Ok((existing_expression.group_id, existing_expression.id)); + ) -> OptimizerResult< + Result< + (Self::GroupId, Self::LogicalExpressionId), + (Self::GroupId, Self::LogicalExpressionId), + >, + > { + // Check if the expression already exists in the memo table. + if let Some(existing_id) = self + .is_duplicate_logical_expression(&logical_expression) + .await? + { + let expr = self.get_logical_expression(existing_id).await?; + return Ok(Err((expr.group_id, expr.id))); } // The expression does not exist yet, so we need to create a new group and new expression. @@ -239,6 +253,18 @@ impl Memo for PersistentMemo { .await?; } - Ok((new_expr.group_id, new_expr.id)) + // Insert the fingerprint of the logical expression. + let hash = hash_expression(new_expr.kind, &new_expr.data); + let fingerprint = fingerprint::ActiveModel { + id: NotSet, + logical_expression_id: Set(new_expr.id), + kind: Set(new_expr.kind), + hash: Set(hash), + }; + let _ = fingerprint::Entity::insert(fingerprint) + .exec(&self.db) + .await?; + + Ok(Ok((new_expr.group_id, new_expr.id))) } } diff --git a/optd-mvp/src/memo/persistent/mod.rs b/optd-mvp/src/memo/persistent/mod.rs new file mode 100644 index 0000000..ae2577a --- /dev/null +++ b/optd-mvp/src/memo/persistent/mod.rs @@ -0,0 +1,66 @@ +//! This module contains the definition and implementation of the [`PersistentMemo`] type, which +//! implements the `Memo` trait and supports memo table operations necessary for query optimization. + +use crate::{ + entities::{prelude::*, *}, + DATABASE_URL, +}; +use sea_orm::*; + +#[cfg(test)] +mod tests; + +/// A persistent memo table, backed by a database on disk. +/// +/// TODO more docs. +pub struct PersistentMemo { + /// This `PersistentMemo` is reliant on the SeaORM [`DatabaseConnection`] that stores all of the + /// objects needed for query optimization. + db: DatabaseConnection, +} + +impl PersistentMemo { + /// Creates a new `PersistentMemo` struct by connecting to a database defined at + /// [`DATABASE_URL`]. + /// + /// TODO remove dead code and write docs. + #[allow(dead_code)] + pub async fn new() -> Self { + Self { + db: Database::connect(DATABASE_URL).await.unwrap(), + } + } + + /// Since there is no asynchronous drop yet in Rust, we must do this manually. + /// + /// TODO remove dead code and write docs. + #[allow(dead_code)] + pub async fn cleanup(&self) { + cascades_group::Entity::delete_many() + .exec(&self.db) + .await + .unwrap(); + fingerprint::Entity::delete_many() + .exec(&self.db) + .await + .unwrap(); + logical_expression::Entity::delete_many() + .exec(&self.db) + .await + .unwrap(); + logical_children::Entity::delete_many() + .exec(&self.db) + .await + .unwrap(); + physical_expression::Entity::delete_many() + .exec(&self.db) + .await + .unwrap(); + physical_children::Entity::delete_many() + .exec(&self.db) + .await + .unwrap(); + } +} + +mod implementation; diff --git a/optd-mvp/src/memo/persistent/tests.rs b/optd-mvp/src/memo/persistent/tests.rs new file mode 100644 index 0000000..7158b30 --- /dev/null +++ b/optd-mvp/src/memo/persistent/tests.rs @@ -0,0 +1,36 @@ +use super::*; +use crate::{expression::*, memo::Memo}; + +/// Tests is exact expression matches are detected and handled by the memo table. +#[ignore] +#[tokio::test] +async fn test_simple_duplicates() { + let memo = PersistentMemo::new().await; + memo.cleanup().await; + + let scan = scan("(a int, b int)".to_string()); + let scan1 = scan.clone(); + let scan2 = scan.clone(); + + let res0 = memo + .add_logical_expression(scan.into(), &[]) + .await + .unwrap() + .ok(); + let res1 = memo + .add_logical_expression(scan1.into(), &[]) + .await + .unwrap() + .err(); + let res2 = memo + .add_logical_expression(scan2.into(), &[]) + .await + .unwrap() + .err(); + + assert_eq!(res0, res1); + assert_eq!(res0, res2); + assert_eq!(res1, res2); + + memo.cleanup().await; +} diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_fingerprint.rs b/optd-mvp/src/migrator/memo/m20241127_000001_fingerprint.rs new file mode 100644 index 0000000..4a828b8 --- /dev/null +++ b/optd-mvp/src/migrator/memo/m20241127_000001_fingerprint.rs @@ -0,0 +1,49 @@ +//! An entity representing a logical expression fingerprint. +//! +//! TODO write docs. + +use crate::migrator::memo::logical_expression::LogicalExpression; +use sea_orm_migration::{prelude::*, schema::*}; + +#[derive(DeriveIden)] +pub enum Fingerprint { + Table, + Id, + LogicalExpressionId, + Kind, + Hash, +} + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Fingerprint::Table) + .if_not_exists() + .col(pk_auto(Fingerprint::Id)) + .col(unsigned(Fingerprint::LogicalExpressionId)) + .foreign_key( + ForeignKey::create() + .from(Fingerprint::Table, Fingerprint::LogicalExpressionId) + .to(LogicalExpression::Table, LogicalExpression::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .col(small_unsigned(Fingerprint::Kind)) + .col(big_unsigned(Fingerprint::Hash)) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Fingerprint::Table).to_owned()) + .await + } +} diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_logical_expression.rs b/optd-mvp/src/migrator/memo/m20241127_000001_logical_expression.rs index 3682032..57356cf 100644 --- a/optd-mvp/src/migrator/memo/m20241127_000001_logical_expression.rs +++ b/optd-mvp/src/migrator/memo/m20241127_000001_logical_expression.rs @@ -19,21 +19,23 @@ //! Each `logical_expression` has a unique primary key ID, but it holds little importance other than //! helping distinguish between two different expressions. //! -//! The more interesting column is the `fingerprint` column, in which we store a hashed fingerprint -//! value that can be used to efficiently check equality between two potentially equivalent logical -//! expressions (hash-consing). See ???FIXME??? for more information on expression fingerprints. -//! //! Finally, since there are many different types of operators, we store a variant tag and a data //! column as JSON to represent the semi-structured data fields of logical operators. //! //! # Entity Relationships //! -//! The only relationship that `logical_expression` has is to [`cascades_group`]. It has **both** a +//! The main relationship that `logical_expression` has is to [`cascades_group`]. It has **both** a //! one-to-many **and** a many-to-many relationship with [`cascades_group`], and you can see more //! details about this in the module-level documentation for [`cascades_group`]. //! +//! The other relationship that `logical_expression` has is to [`fingerprint`]. This table stores +//! 1 or more fingerprints for every (unique) logical expression. The reason we have multiple +//! fingerprints is that an expression can belong to multiple groups during the exploration phase +//! before the merging of groups. +//! //! [`cascades_group`]: super::cascades_group //! [`physical_expression`]: super::physical_expression +//! [`fingerprint`]: super::fingerprint use crate::migrator::memo::cascades_group::CascadesGroup; use sea_orm_migration::{prelude::*, schema::*}; @@ -43,7 +45,6 @@ pub enum LogicalExpression { Table, Id, GroupId, - Fingerprint, Kind, Data, } @@ -68,7 +69,6 @@ impl MigrationTrait for Migration { .on_delete(ForeignKeyAction::Cascade) .on_update(ForeignKeyAction::Cascade), ) - .col(big_unsigned(LogicalExpression::Fingerprint)) .col(small_integer(LogicalExpression::Kind)) .col(json(LogicalExpression::Data)) .to_owned(), diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_physical_expression.rs b/optd-mvp/src/migrator/memo/m20241127_000001_physical_expression.rs index 7653112..1e66195 100644 --- a/optd-mvp/src/migrator/memo/m20241127_000001_physical_expression.rs +++ b/optd-mvp/src/migrator/memo/m20241127_000001_physical_expression.rs @@ -20,9 +20,10 @@ //! Each `physical_expression` has a unique primary key ID, and other tables will store a foreign //! key reference to a specific `physical_expression`s. //! -//! The more interesting column is the `fingerprint` column, in which we store a hashed fingerprint -//! value that can be used to efficiently check equality between two potentially equivalent physical -//! expressions (hash-consing). See ???FIXME??? for more information on expression fingerprints. +//! Note that `physical_expression` does **not** store a fingerprint. Remember that we want to +//! detect duplicates in the logical exploration phase. If there are no duplicate logical +//! expressions in the memo table, then there cannot be any duplicate physical expressions, which +//! are derived from said deduplicated logical expressions. //! //! Finally, since there are many different types of operators, we store a variant tag and a data //! column as JSON to represent the semi-structured data fields of logical operators. @@ -44,7 +45,6 @@ pub enum PhysicalExpression { Table, Id, GroupId, - Fingerprint, Kind, Data, } @@ -69,7 +69,6 @@ impl MigrationTrait for Migration { .on_delete(ForeignKeyAction::Cascade) .on_update(ForeignKeyAction::Cascade), ) - .col(big_unsigned(PhysicalExpression::Fingerprint)) .col(small_integer(PhysicalExpression::Kind)) .col(json(PhysicalExpression::Data)) .to_owned(), diff --git a/optd-mvp/src/migrator/memo/mod.rs b/optd-mvp/src/migrator/memo/mod.rs index 8ed9390..7a60c9b 100644 --- a/optd-mvp/src/migrator/memo/mod.rs +++ b/optd-mvp/src/migrator/memo/mod.rs @@ -2,12 +2,14 @@ //! optimization framework. pub(crate) mod m20241127_000001_cascades_group; +pub(crate) mod m20241127_000001_fingerprint; pub(crate) mod m20241127_000001_logical_children; pub(crate) mod m20241127_000001_logical_expression; pub(crate) mod m20241127_000001_physical_children; pub(crate) mod m20241127_000001_physical_expression; pub(crate) use m20241127_000001_cascades_group as cascades_group; +pub(crate) use m20241127_000001_fingerprint as fingerprint; pub(crate) use m20241127_000001_logical_children as logical_children; pub(crate) use m20241127_000001_logical_expression as logical_expression; pub(crate) use m20241127_000001_physical_children as physical_children; diff --git a/optd-mvp/src/migrator/mod.rs b/optd-mvp/src/migrator/mod.rs index 179c406..0945423 100644 --- a/optd-mvp/src/migrator/mod.rs +++ b/optd-mvp/src/migrator/mod.rs @@ -9,6 +9,7 @@ impl MigratorTrait for Migrator { fn migrations() -> Vec> { vec![ Box::new(memo::cascades_group::Migration), + Box::new(memo::fingerprint::Migration), Box::new(memo::logical_expression::Migration), Box::new(memo::logical_children::Migration), Box::new(memo::physical_expression::Migration), From 0a0af6d547e6ed9e348992a2cadbd21a9eb96bf8 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Sat, 30 Nov 2024 11:52:46 -0500 Subject: [PATCH 06/10] huge refactor of persistent memo implementation This commit completely refactors the memo table, removing the `Memo` trait and instead placing all methods directly on the `PersistentMemo` structure itself. This also cleans up some code in other places. --- Cargo.lock | 10 + optd-mvp/Cargo.toml | 2 + optd-mvp/src/entities/logical_children.rs | 2 +- optd-mvp/src/entities/prelude.rs | 2 + optd-mvp/src/expression/logical_expression.rs | 107 +++- .../src/expression/physical_expression.rs | 45 +- optd-mvp/src/lib.rs | 12 - optd-mvp/src/memo/interface.rs | 176 ------- optd-mvp/src/memo/mod.rs | 32 +- .../src/memo/persistent/implementation.rs | 496 ++++++++++++------ optd-mvp/src/memo/persistent/mod.rs | 50 +- optd-mvp/src/memo/persistent/tests.rs | 146 +++++- .../memo/m20241127_000001_logical_children.rs | 2 +- 13 files changed, 603 insertions(+), 479 deletions(-) delete mode 100644 optd-mvp/src/memo/interface.rs diff --git a/Cargo.lock b/Cargo.lock index 3059383..8acb13b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -710,6 +710,15 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.14.4" @@ -1146,6 +1155,7 @@ version = "0.1.0" dependencies = [ "async-stream", "async-trait", + "fxhash", "sea-orm", "sea-orm-migration", "serde", diff --git a/optd-mvp/Cargo.toml b/optd-mvp/Cargo.toml index 3b72407..f4a3a62 100644 --- a/optd-mvp/Cargo.toml +++ b/optd-mvp/Cargo.toml @@ -20,8 +20,10 @@ serde_json = "1.0.118" # Support `Hash` on `serde_json::Value` in "1.0.118". tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } trait-variant = "0.1.2" # Support `make(Send)` syntax in "0.1.2". thiserror = "2.0" +fxhash = "0.2" # Pin more recent versions for `-Zminimal-versions`. async-trait = "0.1.43" # Remove lifetime parameter from "0.1.42". async-stream = "0.3.1" # Fix unsatisfied trait bound from "0.3.0". strum = "0.26.0" # Fix `std::marker::Sized` from "0.25.0". + diff --git a/optd-mvp/src/entities/logical_children.rs b/optd-mvp/src/entities/logical_children.rs index 120641f..067eaa7 100644 --- a/optd-mvp/src/entities/logical_children.rs +++ b/optd-mvp/src/entities/logical_children.rs @@ -23,7 +23,7 @@ pub enum Relation { CascadesGroup, #[sea_orm( belongs_to = "super::logical_expression::Entity", - from = "Column::GroupId", + from = "Column::LogicalExpressionId", to = "super::logical_expression::Column::Id", on_update = "Cascade", on_delete = "Cascade" diff --git a/optd-mvp/src/entities/prelude.rs b/optd-mvp/src/entities/prelude.rs index 5619363..bf6879b 100644 --- a/optd-mvp/src/entities/prelude.rs +++ b/optd-mvp/src/entities/prelude.rs @@ -1,5 +1,7 @@ //! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0 +#![allow(unused_imports)] + pub use super::cascades_group::Entity as CascadesGroup; pub use super::fingerprint::Entity as Fingerprint; pub use super::logical_children::Entity as LogicalChildren; diff --git a/optd-mvp/src/expression/logical_expression.rs b/optd-mvp/src/expression/logical_expression.rs index c87b055..7c3362d 100644 --- a/optd-mvp/src/expression/logical_expression.rs +++ b/optd-mvp/src/expression/logical_expression.rs @@ -1,37 +1,91 @@ //! Definition of logical expressions / relations in the Cascades query optimization framework. //! -//! FIXME: All fields are placeholders, and group IDs are just represented as i32 for now. -//! FIXME: Representation needs to know how to "rewrite" child group IDs to whatever a fingerprint -//! will need. +//! FIXME: All fields are placeholders. //! -//! TODO figure out if each relation should be in a different submodule. +//! TODO Remove dead code. +//! TODO Figure out if each relation should be in a different submodule. //! TODO This entire file is a WIP. -use crate::entities::*; +#![allow(dead_code)] + +use crate::{entities::*, memo::GroupId}; +use fxhash::hash; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum LogicalExpression { Scan(Scan), Filter(Filter), Join(Join), } -#[derive(Serialize, Deserialize, Clone, Debug)] +/// FIXME: Figure out how to make everything unsigned instead of signed. +impl LogicalExpression { + pub fn kind(&self) -> i16 { + match self { + LogicalExpression::Scan(_) => 0, + LogicalExpression::Filter(_) => 1, + LogicalExpression::Join(_) => 2, + } + } + + /// Definitions of custom fingerprinting strategies for each kind of logical expression. + pub fn fingerprint(&self) -> i64 { + self.fingerprint_with_rewrite(&[]) + } + + /// Calculates the fingerprint of a given expression, but replaces all of the children group IDs + /// with a new group ID if it is listed in the input `rewrites` list. + /// + /// TODO Allow each expression to implement a trait that does this. + pub fn fingerprint_with_rewrite(&self, rewrites: &[(GroupId, GroupId)]) -> i64 { + // Closure that rewrites a group ID if needed. + let rewrite = |x: GroupId| { + if rewrites.is_empty() { + return x; + } + + if let Some(i) = rewrites.iter().position(|(curr, _new)| &x == curr) { + assert_eq!(rewrites[i].0, x); + rewrites[i].1 + } else { + x + } + }; + + let kind = self.kind() as u16 as usize; + let hash = match self { + LogicalExpression::Scan(scan) => hash(scan.table_schema.as_str()), + LogicalExpression::Filter(filter) => { + hash(&rewrite(filter.child).0) ^ hash(filter.expression.as_str()) + } + LogicalExpression::Join(join) => { + hash(&rewrite(join.left).0) + ^ hash(&rewrite(join.right).0) + ^ hash(join.expression.as_str()) + } + }; + + // Mask out the bottom 16 bits of `hash` and replace them with `kind`. + ((hash & !0xFFFF) | kind) as i64 + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct Scan { table_schema: String, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct Filter { - child: i32, + child: GroupId, expression: String, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct Join { - left: i32, - right: i32, + left: GroupId, + right: GroupId, expression: String, } @@ -71,17 +125,18 @@ impl From for logical_expression::Model { } } + let kind = value.kind(); match value { LogicalExpression::Scan(scan) => create_logical_expression( - 0, + kind, serde_json::to_value(scan).expect("unable to serialize logical `Scan`"), ), LogicalExpression::Filter(filter) => create_logical_expression( - 1, + kind, serde_json::to_value(filter).expect("unable to serialize logical `Filter`"), ), LogicalExpression::Join(join) => create_logical_expression( - 2, + kind, serde_json::to_value(join).expect("unable to serialize logical `Join`"), ), } @@ -94,24 +149,28 @@ pub use build::*; #[cfg(test)] mod build { use super::*; - use crate::expression::Expression; + use crate::expression::LogicalExpression; - pub fn scan(table_schema: String) -> Expression { - Expression::Logical(LogicalExpression::Scan(Scan { table_schema })) + pub fn scan(table_schema: String) -> LogicalExpression { + LogicalExpression::Scan(Scan { table_schema }) } - pub fn filter(child_group: i32, expression: String) -> Expression { - Expression::Logical(LogicalExpression::Filter(Filter { + pub fn filter(child_group: GroupId, expression: String) -> LogicalExpression { + LogicalExpression::Filter(Filter { child: child_group, expression, - })) + }) } - pub fn join(left_group: i32, right_group: i32, expression: String) -> Expression { - Expression::Logical(LogicalExpression::Join(Join { + pub fn join( + left_group: GroupId, + right_group: GroupId, + expression: String, + ) -> LogicalExpression { + LogicalExpression::Join(Join { left: left_group, right: right_group, expression, - })) + }) } } diff --git a/optd-mvp/src/expression/physical_expression.rs b/optd-mvp/src/expression/physical_expression.rs index 6552a96..5719752 100644 --- a/optd-mvp/src/expression/physical_expression.rs +++ b/optd-mvp/src/expression/physical_expression.rs @@ -1,35 +1,38 @@ //! Definition of physical expressions / operators in the Cascades query optimization framework. //! -//! FIXME: All fields are placeholders, and group IDs are just represented as i32 for now. +//! FIXME: All fields are placeholders. //! -//! TODO figure out if each operator should be in a different submodule. +//! TODO Remove dead code. +//! TODO Figure out if each operator should be in a different submodule. //! TODO This entire file is a WIP. -use crate::entities::*; +#![allow(dead_code)] + +use crate::{entities::*, memo::GroupId}; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum PhysicalExpression { TableScan(TableScan), Filter(PhysicalFilter), HashJoin(HashJoin), } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct TableScan { table_schema: String, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct PhysicalFilter { - child: i32, + child: GroupId, expression: String, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct HashJoin { - left: i32, - right: i32, + left: GroupId, + right: GroupId, expression: String, } @@ -92,24 +95,28 @@ pub use build::*; #[cfg(test)] mod build { use super::*; - use crate::expression::Expression; + use crate::expression::PhysicalExpression; - pub fn table_scan(table_schema: String) -> Expression { - Expression::Physical(PhysicalExpression::TableScan(TableScan { table_schema })) + pub fn table_scan(table_schema: String) -> PhysicalExpression { + PhysicalExpression::TableScan(TableScan { table_schema }) } - pub fn filter(child_group: i32, expression: String) -> Expression { - Expression::Physical(PhysicalExpression::Filter(PhysicalFilter { + pub fn filter(child_group: GroupId, expression: String) -> PhysicalExpression { + PhysicalExpression::Filter(PhysicalFilter { child: child_group, expression, - })) + }) } - pub fn hash_join(left_group: i32, right_group: i32, expression: String) -> Expression { - Expression::Physical(PhysicalExpression::HashJoin(HashJoin { + pub fn hash_join( + left_group: GroupId, + right_group: GroupId, + expression: String, + ) -> PhysicalExpression { + PhysicalExpression::HashJoin(HashJoin { left: left_group, right: right_group, expression, - })) + }) } } diff --git a/optd-mvp/src/lib.rs b/optd-mvp/src/lib.rs index 98c5f11..506eee4 100644 --- a/optd-mvp/src/lib.rs +++ b/optd-mvp/src/lib.rs @@ -37,15 +37,3 @@ pub type OptimizerResult = Result; pub async fn migrate(db: &DatabaseConnection) -> Result<(), DbErr> { Migrator::refresh(db).await } - -/// Helper function for hashing expression data. -/// -/// TODO remove this. -fn hash_expression(kind: i16, data: &serde_json::Value) -> i64 { - use std::hash::{DefaultHasher, Hash, Hasher}; - - let mut hasher = DefaultHasher::new(); - kind.hash(&mut hasher); - data.hash(&mut hasher); - hasher.finish() as i64 -} diff --git a/optd-mvp/src/memo/interface.rs b/optd-mvp/src/memo/interface.rs deleted file mode 100644 index cb6c76d..0000000 --- a/optd-mvp/src/memo/interface.rs +++ /dev/null @@ -1,176 +0,0 @@ -//! This module defines the [`Memo`] trait, which defines shared behavior of all memo table that can -//! be used for query optimization in the Cascades framework. - -use crate::OptimizerResult; -use thiserror::Error; - -/// The different kinds of errors that might occur while running operations on a memo table. -#[derive(Error, Debug)] -pub enum MemoError { - #[error("unknown group ID {0}")] - UnknownGroup(i32), - #[error("unknown logical expression ID {0}")] - UnknownLogicalExpression(i32), - #[error("unknown physical expression ID {0}")] - UnknownPhysicalExpression(i32), - #[error("invalid expression encountered")] - InvalidExpression, -} - -/// A trait representing an implementation of a memoization table. -/// -/// Note that we use [`trait_variant`] here in order to add bounds on every method. -/// See this [blog post]( -/// https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits.html#async-fn-in-public-traits) -/// for more information. -/// -/// TODO remove dead code. -#[allow(dead_code)] -#[trait_variant::make(Send)] -pub trait Memo { - /// A type representing a group in the Cascades framework. - type Group; - /// A type representing a unique identifier for a group. - type GroupId; - /// A type representing a logical expression. - type LogicalExpression; - /// A type representing a unique identifier for a logical expression. - type LogicalExpressionId; - /// A type representing a physical expression. - type PhysicalExpression; - /// A type representing a unique identifier for a physical expression. - type PhysicalExpressionId; - - /// Retrieves a [`Self::Group`] given a [`Self::GroupId`]. - /// - /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. - async fn get_group(&self, group_id: Self::GroupId) -> OptimizerResult; - - /// Retrieves a [`Self::LogicalExpression`] given a [`Self::LogicalExpressionId`]. - /// - /// If the logical expression does not exist, returns a [`MemoError::UnknownLogicalExpression`] - /// error. - async fn get_logical_expression( - &self, - logical_expression_id: Self::LogicalExpressionId, - ) -> OptimizerResult; - - /// Retrieves a [`Self::PhysicalExpression`] given a [`Self::PhysicalExpressionId`]. - /// - /// If the physical expression does not exist, returns a - /// [`MemoError::UnknownPhysicalExpression`] error. - async fn get_physical_expression( - &self, - physical_expression_id: Self::PhysicalExpressionId, - ) -> OptimizerResult; - - /// Retrieves all of the logical expression "children" IDs of a group. - /// - /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. - async fn get_logical_children( - &self, - group_id: Self::GroupId, - ) -> OptimizerResult>; - - /// Retrieves all of the physical expression "children" IDs of a group. - /// - /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. - async fn get_physical_children( - &self, - group_id: Self::GroupId, - ) -> OptimizerResult>; - - /// Checks if a given logical expression is a duplicate / already exists in the memo table. - /// - /// In order to prevent a large amount of duplicate work, the memo table must support duplicate - /// expression detection. - /// - /// Returns `Some(expression_id)` if the memo table detects that the expression already exists, - /// and `None` otherwise. - async fn is_duplicate_logical_expression( - &self, - logical_expression: &Self::LogicalExpression, - ) -> OptimizerResult>; - - /// Updates / replaces a group's best physical plan (winner). Optionally returns the previous - /// winner's physical expression ID. - /// - /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. - async fn update_group_winner( - &self, - group_id: Self::GroupId, - physical_expression_id: Self::PhysicalExpressionId, - ) -> OptimizerResult>; - - /// Adds a physical expression to an existing group via its [`Self::GroupId`]. - /// - /// The caller is required to pass in a slice of `GroupId` that represent the child groups of - /// the input expression. - /// - /// The caller is also required to set the `group_id` field of the input `physical_expression` - /// to be equal to `group_id`, otherwise this function will return a - /// [`MemoError::InvalidExpression`] error. - /// - /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. - /// - /// On successful insertion, returns the ID of the physical expression. - async fn add_physical_expression_to_group( - &self, - group_id: Self::GroupId, - physical_expression: Self::PhysicalExpression, - children: &[Self::GroupId], - ) -> OptimizerResult; - - /// Adds a logical expression to an existing group via its [`Self::GroupId`]. - /// - /// The caller is required to pass in a slice of `GroupId` that represent the child groups of - /// the input expression. - /// - /// The caller is also required to set the `group_id` field of the input `logical_expression` - /// to be equal to `group_id`, otherwise this function will return a - /// [`MemoError::InvalidExpression`] error. - /// - /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. - /// - /// If the memo table detects that the input logical expression is a duplicate expression, it - /// will **not** insert the expression into the memo table. Instead, it will return an - /// `Ok(Err(expression_id))`, which is a unique identifier of the expression that the input is a - /// duplicate of. The caller can use this ID to retrieve the group the original belongs to. - /// - /// If the memo table detects that the input is unique, it will insert the expression into the - /// input group and return an `Ok(Ok(expression_id))`. - async fn add_logical_expression_to_group( - &self, - group_id: Self::GroupId, - logical_expression: Self::LogicalExpression, - children: &[Self::GroupId], - ) -> OptimizerResult>; - - /// Adds a new logical expression into the memo table, creating a new group if the expression - /// does not already exist. - /// - /// The caller is required to pass in a slice of `GroupId` that represent the child groups of - /// the input expression. - /// - /// The [`Self::LogicalExpression`] type should have some sort of mechanism for checking if - /// the expression has been seen before, and if it has already been created, then the parent - /// group ID should also be retrievable. - /// - /// If the expression already exists, then this function will return the [`Self::GroupId`] of - /// the parent group and the corresponding (already existing) [`Self::LogicalExpressionId`]. It - /// will also completely ignore the group ID field of the input expression as well as ignore the - /// input slice of child groups. - /// - /// If the expression does not exist, this function will create a new group and a new - /// expression, returning brand new IDs for both. - async fn add_logical_expression( - &self, - expression: Self::LogicalExpression, - children: &[Self::LogicalExpressionId], - ) -> OptimizerResult< - Result< - (Self::GroupId, Self::LogicalExpressionId), - (Self::GroupId, Self::LogicalExpressionId), - >, - >; -} diff --git a/optd-mvp/src/memo/mod.rs b/optd-mvp/src/memo/mod.rs index 5253352..fbf23a2 100644 --- a/optd-mvp/src/memo/mod.rs +++ b/optd-mvp/src/memo/mod.rs @@ -3,7 +3,33 @@ //! //! TODO more docs. -mod persistent; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +/// A new type of an integer identifying a unique group. +#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)] +#[serde(transparent)] +pub struct GroupId(pub i32); + +/// A new type of an integer identifying a unique logical expression. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct LogicalExpressionId(pub i32); -mod interface; -pub use interface::{Memo, MemoError}; +/// A new type of an integer identifying a unique physical expression. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct PhysicalExpressionId(pub i32); + +/// The different kinds of errors that might occur while running operations on a memo table. +#[derive(Error, Debug)] +pub enum MemoError { + #[error("unknown group ID {0:?}")] + UnknownGroup(GroupId), + #[error("unknown logical expression ID {0:?}")] + UnknownLogicalExpression(LogicalExpressionId), + #[error("unknown physical expression ID {0:?}")] + UnknownPhysicalExpression(PhysicalExpressionId), + #[error("invalid expression encountered")] + InvalidExpression, +} + +mod persistent; diff --git a/optd-mvp/src/memo/persistent/implementation.rs b/optd-mvp/src/memo/persistent/implementation.rs index 4c06c4e..4fc7048 100644 --- a/optd-mvp/src/memo/persistent/implementation.rs +++ b/optd-mvp/src/memo/persistent/implementation.rs @@ -1,228 +1,370 @@ -//! This module contains the implementation of the [`Memo`] trait for [`PersistentMemo`]. +//! This module contains the implementation of [`PersistentMemo`]. +//! +//! TODO For parallelism, almost all of these methods need to be under transactions. +//! TODO Write more docs. +//! TODO Remove dead code. -use super::*; +#![allow(dead_code)] + +use super::PersistentMemo; use crate::{ - hash_expression, - memo::{Memo, MemoError}, - OptimizerResult, + entities::*, + expression::{LogicalExpression, PhysicalExpression}, + memo::{GroupId, LogicalExpressionId, MemoError, PhysicalExpressionId}, + OptimizerResult, DATABASE_URL, +}; +use sea_orm::{ + entity::prelude::*, + entity::{IntoActiveModel, NotSet, Set}, + Database, }; -impl Memo for PersistentMemo { - type Group = cascades_group::Model; - type GroupId = i32; - type LogicalExpression = logical_expression::Model; - type LogicalExpressionId = i32; - type PhysicalExpression = physical_expression::Model; - type PhysicalExpressionId = i32; +impl PersistentMemo { + /// Creates a new `PersistentMemo` struct by connecting to a database defined at + /// [`DATABASE_URL`]. + pub async fn new() -> Self { + Self { + db: Database::connect(DATABASE_URL).await.unwrap(), + } + } + + /// Deletes all objects in the backing database. + /// + /// Since there is no asynchronous drop yet in Rust, in order to drop all objects in the + /// database, the user must call this manually. + pub async fn cleanup(&self) { + macro_rules! delete_all { + ($($module: ident),+ $(,)?) => { + $( + $module::Entity::delete_many() + .exec(&self.db) + .await + .unwrap(); + )+ + }; + } + + delete_all! { + cascades_group, + fingerprint, + logical_expression, + logical_children, + physical_expression, + physical_children + }; + } - async fn get_group(&self, group_id: Self::GroupId) -> OptimizerResult { - Ok(CascadesGroup::find_by_id(group_id) + /// Retrieves a [`cascades_group::Model`] given its ID. + /// + /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. + /// + /// FIXME: use an in-memory representation of a group instead. + pub async fn get_group(&self, group_id: GroupId) -> OptimizerResult { + Ok(cascades_group::Entity::find_by_id(group_id.0) .one(&self.db) .await? .ok_or(MemoError::UnknownGroup(group_id))?) } - async fn get_logical_expression( + /// Retrieves a [`physical_expression::Model`] given a [`PhysicalExpressionId`]. + /// + /// If the physical expression does not exist, returns a + /// [`MemoError::UnknownPhysicalExpression`] error. + pub async fn get_physical_expression( &self, - logical_expression_id: Self::LogicalExpressionId, - ) -> OptimizerResult { - Ok(LogicalExpression::find_by_id(logical_expression_id) + physical_expression_id: PhysicalExpressionId, + ) -> OptimizerResult<(GroupId, PhysicalExpression)> { + // Lookup the entity in the database via the unique expression ID. + let model = physical_expression::Entity::find_by_id(physical_expression_id.0) .one(&self.db) .await? - .ok_or(MemoError::UnknownLogicalExpression(logical_expression_id))?) + .ok_or(MemoError::UnknownPhysicalExpression(physical_expression_id))?; + + let group_id = GroupId(model.group_id); + let expr = model.into(); + + Ok((group_id, expr)) } - async fn get_physical_expression( + /// Retrieves a [`logical_expression::Model`] given its [`LogicalExpressionId`]. + /// + /// If the logical expression does not exist, returns a [`MemoError::UnknownLogicalExpression`] + /// error. + pub async fn get_logical_expression( &self, - physical_expression_id: Self::PhysicalExpressionId, - ) -> OptimizerResult { - Ok(PhysicalExpression::find_by_id(physical_expression_id) + logical_expression_id: LogicalExpressionId, + ) -> OptimizerResult<(GroupId, LogicalExpression)> { + // Lookup the entity in the database via the unique expression ID. + let model = logical_expression::Entity::find_by_id(logical_expression_id.0) .one(&self.db) .await? - .ok_or(MemoError::UnknownPhysicalExpression(physical_expression_id))?) + .ok_or(MemoError::UnknownLogicalExpression(logical_expression_id))?; + + let group_id = GroupId(model.group_id); + let expr = model.into(); + + Ok((group_id, expr)) } - async fn get_logical_children( + /// Retrieves all of the logical expression "children" IDs of a group. + /// + /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. + /// + /// FIXME: `find_related` does not work for some reason, have to use manual `filter`. + pub async fn get_logical_children( &self, - group_id: Self::GroupId, - ) -> OptimizerResult> { - // First retrieve the group record, and then find all related logical expressions. - Ok(self - .get_group(group_id) - .await? - .find_related(LogicalChildren) + group_id: GroupId, + ) -> OptimizerResult> { + // Search for expressions that have the given parent group ID. + let children = logical_expression::Entity::find() + .filter(logical_expression::Column::GroupId.eq(group_id.0)) .all(&self.db) .await? .into_iter() - .map(|m| m.logical_expression_id) - .collect()) + .map(|m| LogicalExpressionId(m.id)) + .collect(); + + Ok(children) } - async fn get_physical_children( + /// Retrieves all of the physical expression "children" IDs of a group. + /// + /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. + pub async fn get_physical_children( &self, - group_id: Self::GroupId, - ) -> OptimizerResult> { - // First retrieve the group record, and then find all related physical expressions. - Ok(self - .get_group(group_id) - .await? - .find_related(PhysicalChildren) + group_id: GroupId, + ) -> OptimizerResult> { + // Search for expressions that have the given parent group ID. + let children = physical_expression::Entity::find() + .filter(physical_expression::Column::GroupId.eq(group_id.0)) .all(&self.db) .await? .into_iter() - .map(|m| m.physical_expression_id) - .collect()) - } - - /// FIXME Check that all of the children are root groups? - async fn is_duplicate_logical_expression( - &self, - logical_expression: &Self::LogicalExpression, - ) -> OptimizerResult> { - // Lookup all expressions that have the same fingerprint and kind. There may be false - // positives, but we will check for those next. - let kind = logical_expression.kind; - let fingerprint = hash_expression(kind, &logical_expression.data); - - let potential_matches = Fingerprint::find() - .filter(fingerprint::Column::Hash.eq(fingerprint)) - .filter(fingerprint::Column::Kind.eq(kind)) - .all(&self.db) - .await?; - - if potential_matches.is_empty() { - return Ok(None); - } - - let mut match_id = None; - for potential_match in potential_matches { - let expr_id = potential_match.logical_expression_id; - let expr = self.get_logical_expression(expr_id).await?; - - if expr.data == logical_expression.data { - // There should be at most one duplicate expression. - match_id = Some(expr_id); - break; - } - } + .map(|m| PhysicalExpressionId(m.id)) + .collect(); - Ok(match_id) + Ok(children) } + /// Updates / replaces a group's best physical plan (winner). Optionally returns the previous + /// winner's physical expression ID. + /// + /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. + /// /// FIXME: In the future, this should first check that we aren't overwriting a winner that was - /// updated from another thread. - async fn update_group_winner( + /// updated from another thread by comparing against the cost of the plan. + pub async fn update_group_winner( &self, - group_id: Self::GroupId, - physical_expression_id: Self::PhysicalExpressionId, - ) -> OptimizerResult> { - // First retrieve the group record, and then use an `ActiveModel` to update it. + group_id: GroupId, + physical_expression_id: PhysicalExpressionId, + ) -> OptimizerResult> { + // First retrieve the group record. let mut group = self.get_group(group_id).await?.into_active_model(); - let old_id = group.winner; - group.winner = Set(Some(physical_expression_id)); + // Update the group to point to the new winner. + let old_id = group.winner; + group.winner = Set(Some(physical_expression_id.0)); group.update(&self.db).await?; - // The old value must be set (`None` still means it has been set). - let old = old_id.unwrap(); + // Note that the `unwrap` here is unwrapping the `ActiveValue`, not the `Option`. + let old = old_id.unwrap().map(PhysicalExpressionId); Ok(old) } - async fn add_physical_expression_to_group( + /// Adds a logical expression to an existing group via its ID. + /// + /// The caller is required to pass in a slice of [`GroupId`] that represent the child groups of + /// the input expression. + /// + /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. + /// + /// If the memo table detects that the input logical expression is a duplicate expression, this + /// function will **not** insert the expression into the memo table. Instead, it will return an + /// `Ok(Err(expression_id))`, which is a unique identifier of the expression that the input is a + /// duplicate of. The caller can use this ID to retrieve the group the original belongs to. + /// + /// If the memo table detects that the input is unique, it will insert the expression into the + /// input group and return an `Ok(Ok(expression_id))`. + /// + /// FIXME Check that all of the children are reduced groups? + pub async fn add_logical_expression_to_group( &self, - group_id: Self::GroupId, - physical_expression: Self::PhysicalExpression, - children: &[Self::GroupId], - ) -> OptimizerResult { - if physical_expression.group_id != group_id { - Err(MemoError::InvalidExpression)? + group_id: GroupId, + logical_expression: LogicalExpression, + children: &[GroupId], + ) -> OptimizerResult> { + // Check if the expression already exists anywhere in the memo table. + if let Some(existing_id) = self + .is_duplicate_logical_expression(&logical_expression) + .await? + { + return Ok(Err(existing_id)); } // Check if the group actually exists. let _ = self.get_group(group_id).await?; + // Insert the expression. + let model: logical_expression::Model = logical_expression.into(); + let mut active_model = model.into_active_model(); + active_model.group_id = Set(group_id.0); + active_model.id = NotSet; + let new_model = active_model.insert(&self.db).await?; + + let expr_id = new_model.id; + // Insert the child groups of the expression into the junction / children table. - if !children.is_empty() { - PhysicalChildren::insert_many(children.iter().copied().map(|group_id| { - physical_children::ActiveModel { - physical_expression_id: Set(physical_expression.id), - group_id: Set(group_id), - } - })) - .exec(&self.db) - .await?; - } + logical_children::Entity::insert_many(children.iter().copied().map(|child_id| { + logical_children::ActiveModel { + logical_expression_id: Set(expr_id), + group_id: Set(child_id.0), + } + })) + .on_empty_do_nothing() + .exec(&self.db) + .await?; - // Insert the expression. - let res = physical_expression - .into_active_model() - .insert(&self.db) + // Finally, insert the fingerprint of the logical expression as well. + let new_expr: LogicalExpression = new_model.into(); + let kind = new_expr.kind(); + let hash = new_expr.fingerprint(); + + let fingerprint = fingerprint::ActiveModel { + id: NotSet, + logical_expression_id: Set(expr_id), + kind: Set(kind), + hash: Set(hash), + }; + let _ = fingerprint::Entity::insert(fingerprint) + .exec(&self.db) .await?; - Ok(res.id) + Ok(Ok(LogicalExpressionId(expr_id))) } - /// FIXME Check that all of the children are reduced groups? - async fn add_logical_expression_to_group( + /// Adds a physical expression to an existing group via its ID. + /// + /// The caller is required to pass in a slice of [`GroupId`] that represent the child groups of + /// the input expression. + /// + /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. + /// + /// On successful insertion, returns the ID of the physical expression. + pub async fn add_physical_expression_to_group( &self, - group_id: Self::GroupId, - logical_expression: Self::LogicalExpression, - children: &[Self::GroupId], - ) -> OptimizerResult> { - if logical_expression.group_id != group_id { - Err(MemoError::InvalidExpression)? - } - - // Check if the expression already exists in the memo table. - if let Some(existing_id) = self - .is_duplicate_logical_expression(&logical_expression) - .await? - { - return Ok(Err(existing_id)); - } - + group_id: GroupId, + physical_expression: PhysicalExpression, + children: &[GroupId], + ) -> OptimizerResult { // Check if the group actually exists. let _ = self.get_group(group_id).await?; + // Insert the expression. + let model: physical_expression::Model = physical_expression.into(); + let mut active_model = model.into_active_model(); + active_model.group_id = Set(group_id.0); + active_model.id = NotSet; + let new_model = active_model.insert(&self.db).await?; + // Insert the child groups of the expression into the junction / children table. - if !children.is_empty() { - LogicalChildren::insert_many(children.iter().copied().map(|group_id| { - logical_children::ActiveModel { - logical_expression_id: Set(logical_expression.id), - group_id: Set(group_id), - } - })) - .exec(&self.db) + physical_children::Entity::insert_many(children.iter().copied().map(|child_id| { + physical_children::ActiveModel { + physical_expression_id: Set(new_model.id), + group_id: Set(child_id.0), + } + })) + .on_empty_do_nothing() + .exec(&self.db) + .await?; + + Ok(PhysicalExpressionId(new_model.id)) + } + + /// Checks if the given logical expression is a duplicate / already exists in the memo table. + /// + /// In order to prevent a large amount of duplicate work, the memo table must support duplicate + /// expression detection. + /// + /// Returns `Some(expression_id)` if the memo table detects that the expression already exists, + /// and `None` otherwise. + /// + /// This function assumes that the child groups of the expression are currently roots of their + /// group sets. For example, if G1 and G2 should be merged, and G1 is the root, then the input + /// expression should _not_ have G2 as a child, and should be replaced with G1. + /// + /// TODO Check that all of the children are root groups? How to do this? + pub async fn is_duplicate_logical_expression( + &self, + logical_expression: &LogicalExpression, + ) -> OptimizerResult> { + let model: logical_expression::Model = logical_expression.clone().into(); + + // Lookup all expressions that have the same fingerprint and kind. There may be false + // positives, but we will check for those next. + let kind = model.kind; + let fingerprint = logical_expression.fingerprint(); + + // Filter first by the fingerprint, and then the kind. + // FIXME: The kind is already embedded into the fingerprint, so we may not actually need the + // second filter? + let potential_matches = fingerprint::Entity::find() + .filter(fingerprint::Column::Hash.eq(fingerprint)) + .filter(fingerprint::Column::Kind.eq(kind)) + .all(&self.db) .await?; + + if potential_matches.is_empty() { + return Ok(None); } - // Insert the expression. - let res = logical_expression - .into_active_model() - .insert(&self.db) - .await?; + // Now that we have all of the expressions that match the given fingerprint, we need to + // filter out all of the expressions that might have had the same fingerprint but are not + // actually equivalent (hash collisions). + let mut match_id = None; + for potential_match in potential_matches { + let expr_id = LogicalExpressionId(potential_match.logical_expression_id); + let (_, expr) = self.get_logical_expression(expr_id).await?; - Ok(Ok(res.id)) + // Check for an exact match. + if &expr == logical_expression { + match_id = Some(expr_id); + + // There should be at most one duplicate expression, so we can break here. + break; + } + } + + Ok(match_id) } + /// Adds a new group into the memo table via a logical expression, creating a new group if the + /// logical expression does not already exist. + /// + /// The caller is required to pass in a slice of [`GroupId`] that represent the child groups of + /// the input expression. + /// + /// If the expression already exists, then this function will return the [`GroupId`] of the + /// parent group and the corresponding (already existing) [`LogicalExpressionId`]. It will also + /// completely ignore the group ID field of the input expression as well as ignore the input + /// slice of child groups. + /// + /// If the expression does not exist, this function will create a new group and a new + /// expression, returning brand new IDs for both. + /// /// FIXME Check that all of the children are reduced groups? - async fn add_logical_expression( + pub async fn add_group( &self, - logical_expression: Self::LogicalExpression, - children: &[Self::GroupId], - ) -> OptimizerResult< - Result< - (Self::GroupId, Self::LogicalExpressionId), - (Self::GroupId, Self::LogicalExpressionId), - >, - > { + logical_expression: LogicalExpression, + children: &[GroupId], + ) -> OptimizerResult> + { // Check if the expression already exists in the memo table. if let Some(existing_id) = self .is_duplicate_logical_expression(&logical_expression) .await? { - let expr = self.get_logical_expression(existing_id).await?; - return Ok(Err((expr.group_id, expr.id))); + let (group_id, _expr) = self.get_logical_expression(existing_id).await?; + return Ok(Err((group_id, existing_id))); } // The expression does not exist yet, so we need to create a new group and new expression. @@ -232,39 +374,45 @@ impl Memo for PersistentMemo { ..Default::default() }; - // Create a new group. + // Create the new group. let res = cascades_group::Entity::insert(group).exec(&self.db).await?; - // Insert the input expression with the correct `group_id`. - let mut new_expr = logical_expression.into_active_model(); - new_expr.group_id = Set(res.last_insert_id); - new_expr.id = NotSet; - let new_expr = new_expr.insert(&self.db).await?; + // Insert the input expression into the newly created group. + let model: logical_expression::Model = logical_expression.clone().into(); + let mut active_model = model.into_active_model(); + active_model.group_id = Set(res.last_insert_id); + active_model.id = NotSet; + let new_model = active_model.insert(&self.db).await?; + + let group_id = new_model.group_id; + let expr_id = new_model.id; // Insert the child groups of the expression into the junction / children table. - if !children.is_empty() { - LogicalChildren::insert_many(children.iter().copied().map(|group_id| { - logical_children::ActiveModel { - logical_expression_id: Set(new_expr.id), - group_id: Set(group_id), - } - })) - .exec(&self.db) - .await?; - } + logical_children::Entity::insert_many(children.iter().copied().map(|child_id| { + logical_children::ActiveModel { + logical_expression_id: Set(new_model.id), + group_id: Set(child_id.0), + } + })) + .on_empty_do_nothing() + .exec(&self.db) + .await?; + + // Finally, insert the fingerprint of the logical expression as well. + let new_expr: LogicalExpression = new_model.into(); + let kind = new_expr.kind(); + let hash = new_expr.fingerprint(); - // Insert the fingerprint of the logical expression. - let hash = hash_expression(new_expr.kind, &new_expr.data); let fingerprint = fingerprint::ActiveModel { id: NotSet, - logical_expression_id: Set(new_expr.id), - kind: Set(new_expr.kind), + logical_expression_id: Set(expr_id), + kind: Set(kind), hash: Set(hash), }; let _ = fingerprint::Entity::insert(fingerprint) .exec(&self.db) .await?; - Ok(Ok((new_expr.group_id, new_expr.id))) + Ok(Ok((GroupId(group_id), LogicalExpressionId(expr_id)))) } } diff --git a/optd-mvp/src/memo/persistent/mod.rs b/optd-mvp/src/memo/persistent/mod.rs index ae2577a..ed64fc5 100644 --- a/optd-mvp/src/memo/persistent/mod.rs +++ b/optd-mvp/src/memo/persistent/mod.rs @@ -1,11 +1,7 @@ //! This module contains the definition and implementation of the [`PersistentMemo`] type, which //! implements the `Memo` trait and supports memo table operations necessary for query optimization. -use crate::{ - entities::{prelude::*, *}, - DATABASE_URL, -}; -use sea_orm::*; +use sea_orm::DatabaseConnection; #[cfg(test)] mod tests; @@ -19,48 +15,4 @@ pub struct PersistentMemo { db: DatabaseConnection, } -impl PersistentMemo { - /// Creates a new `PersistentMemo` struct by connecting to a database defined at - /// [`DATABASE_URL`]. - /// - /// TODO remove dead code and write docs. - #[allow(dead_code)] - pub async fn new() -> Self { - Self { - db: Database::connect(DATABASE_URL).await.unwrap(), - } - } - - /// Since there is no asynchronous drop yet in Rust, we must do this manually. - /// - /// TODO remove dead code and write docs. - #[allow(dead_code)] - pub async fn cleanup(&self) { - cascades_group::Entity::delete_many() - .exec(&self.db) - .await - .unwrap(); - fingerprint::Entity::delete_many() - .exec(&self.db) - .await - .unwrap(); - logical_expression::Entity::delete_many() - .exec(&self.db) - .await - .unwrap(); - logical_children::Entity::delete_many() - .exec(&self.db) - .await - .unwrap(); - physical_expression::Entity::delete_many() - .exec(&self.db) - .await - .unwrap(); - physical_children::Entity::delete_many() - .exec(&self.db) - .await - .unwrap(); - } -} - mod implementation; diff --git a/optd-mvp/src/memo/persistent/tests.rs b/optd-mvp/src/memo/persistent/tests.rs index 7158b30..f3afea6 100644 --- a/optd-mvp/src/memo/persistent/tests.rs +++ b/optd-mvp/src/memo/persistent/tests.rs @@ -1,36 +1,142 @@ -use super::*; -use crate::{expression::*, memo::Memo}; +use crate::{expression::*, memo::persistent::PersistentMemo}; -/// Tests is exact expression matches are detected and handled by the memo table. +/// Tests that exact expression matches are detected and handled by the memo table. #[ignore] #[tokio::test] -async fn test_simple_duplicates() { +async fn test_simple_logical_duplicates() { let memo = PersistentMemo::new().await; memo.cleanup().await; - let scan = scan("(a int, b int)".to_string()); - let scan1 = scan.clone(); - let scan2 = scan.clone(); + let scan = scan("t1".to_string()); + let scan1a = scan.clone(); + let scan1b = scan.clone(); + let scan2a = scan.clone(); + let scan2b = scan.clone(); - let res0 = memo - .add_logical_expression(scan.into(), &[]) + // Insert a new group and its corresponding expression. + let (group_id, logical_expression_id) = memo.add_group(scan, &[]).await.unwrap().ok().unwrap(); + + // Test `add_logical_expression`. + { + // Attempting to create a new group with a duplicate expression should fail every time. + let (group_id_1a, logical_expression_id_1a) = + memo.add_group(scan1a, &[]).await.unwrap().err().unwrap(); + assert_eq!(group_id, group_id_1a); + assert_eq!(logical_expression_id, logical_expression_id_1a); + + // Try again just in case... + let (group_id_1b, logical_expression_id_1b) = + memo.add_group(scan1b, &[]).await.unwrap().err().unwrap(); + assert_eq!(group_id, group_id_1b); + assert_eq!(logical_expression_id, logical_expression_id_1b); + } + + // Test `add_logical_expression_to_group`. + { + // Attempting to add a duplicate expression into the same group should also fail every time. + let logical_expression_id_2a = memo + .add_logical_expression_to_group(group_id, scan2a, &[]) + .await + .unwrap() + .err() + .unwrap(); + assert_eq!(logical_expression_id, logical_expression_id_2a); + + let logical_expression_id_2b = memo + .add_logical_expression_to_group(group_id, scan2b, &[]) + .await + .unwrap() + .err() + .unwrap(); + assert_eq!(logical_expression_id, logical_expression_id_2b); + } + + memo.cleanup().await; +} + +/// Tests that physical expression are _not_ subject to duplicate detection and elimination. +/// +/// !!! Important !!! Note that this behavior should not actually be seen during query optimization, +/// since if logical expression have been deduplicated, there should not be any duplicate physical +/// expressions as they are derivative of the deduplicated logical expressions. +#[ignore] +#[tokio::test] +async fn test_simple_add_physical_expression() { + let memo = PersistentMemo::new().await; + memo.cleanup().await; + + // Insert a new group and its corresponding expression. + let scan = scan("t1".to_string()); + let (group_id, _) = memo.add_group(scan, &[]).await.unwrap().ok().unwrap(); + + // Insert two identical physical expressions into the _same_ group. + let table_scan_1 = table_scan("t1".to_string()); + let table_scan_2 = table_scan_1.clone(); + + let physical_expression_id_1 = memo + .add_physical_expression_to_group(group_id, table_scan_1, &[]) .await - .unwrap() - .ok(); - let res1 = memo - .add_logical_expression(scan1.into(), &[]) + .unwrap(); + + let physical_expression_id_2 = memo + .add_physical_expression_to_group(group_id, table_scan_2, &[]) + .await + .unwrap(); + + // Since physical expressions do not need duplicate detection, + assert_ne!(physical_expression_id_1, physical_expression_id_2); + + memo.cleanup().await; +} + +/// Tests if the memo tables able to correctly retrieve a group's expressions. +#[ignore] +#[tokio::test] +async fn test_simple_tree() { + let memo = PersistentMemo::new().await; + memo.cleanup().await; + + // Create two scan groups. + let scan1: LogicalExpression = scan("t1".to_string()); + let scan2 = scan("t2".to_string()); + let (scan_id_1, scan_expr_id_1) = memo.add_group(scan1, &[]).await.unwrap().ok().unwrap(); + let (scan_id_2, scan_expr_id_2) = memo.add_group(scan2, &[]).await.unwrap().ok().unwrap(); + + assert_eq!( + memo.get_logical_children(scan_id_1).await.unwrap(), + &[scan_expr_id_1] + ); + assert_eq!( + memo.get_logical_children(scan_id_2).await.unwrap(), + &[scan_expr_id_2] + ); + + // Create two join expression that should be in the same group. + // TODO: Eventually, the predicates will be in their own table, and the predicate representation + // will be a foreign key. For now, we represent them as strings. + let join1 = join(scan_id_1, scan_id_2, "t1.a = t2.b".to_string()); + let join2 = join(scan_id_2, scan_id_1, "t1.a = t2.b".to_string()); + + // Create the group, adding the first expression. + let (join_id, join_expr_id_1) = memo + .add_group(join1, &[scan_id_1, scan_id_2]) .await .unwrap() - .err(); - let res2 = memo - .add_logical_expression(scan2.into(), &[]) + .ok() + .unwrap(); + // Add the second expression. + let join_expr_id_2 = memo + .add_logical_expression_to_group(join_id, join2, &[scan_id_2, scan_id_1]) .await .unwrap() - .err(); + .ok() + .unwrap(); - assert_eq!(res0, res1); - assert_eq!(res0, res2); - assert_eq!(res1, res2); + assert_ne!(join_expr_id_1, join_expr_id_2); + assert_eq!( + memo.get_logical_children(join_id).await.unwrap(), + &[join_expr_id_1, join_expr_id_2] + ); memo.cleanup().await; } diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_logical_children.rs b/optd-mvp/src/migrator/memo/m20241127_000001_logical_children.rs index d0835f4..037a637 100644 --- a/optd-mvp/src/migrator/memo/m20241127_000001_logical_children.rs +++ b/optd-mvp/src/migrator/memo/m20241127_000001_logical_children.rs @@ -40,7 +40,7 @@ impl MigrationTrait for Migration { ) .foreign_key( ForeignKey::create() - .from(LogicalChildren::Table, LogicalChildren::GroupId) + .from(LogicalChildren::Table, LogicalChildren::LogicalExpressionId) .to(LogicalExpression::Table, LogicalExpression::Id) .on_delete(ForeignKeyAction::Cascade) .on_update(ForeignKeyAction::Cascade), From ce1b93d073abab0a247f28151f0dabe28b35c065 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Sat, 30 Nov 2024 16:24:39 -0500 Subject: [PATCH 07/10] add union find and group merging support --- optd-mvp/DESIGN.md | 67 +++++++++ optd-mvp/{README.md => entities.md} | 0 optd-mvp/src/entities/cascades_group.rs | 2 +- optd-mvp/src/memo/mod.rs | 8 + .../src/memo/persistent/implementation.rs | 142 +++++++++++++++--- optd-mvp/src/memo/persistent/tests.rs | 74 ++++++++- .../memo/m20241127_000001_cascades_group.rs | 6 +- .../memo/m20241127_000001_fingerprint.rs | 6 +- 8 files changed, 279 insertions(+), 26 deletions(-) create mode 100644 optd-mvp/DESIGN.md rename optd-mvp/{README.md => entities.md} (100%) diff --git a/optd-mvp/DESIGN.md b/optd-mvp/DESIGN.md new file mode 100644 index 0000000..e50a56c --- /dev/null +++ b/optd-mvp/DESIGN.md @@ -0,0 +1,67 @@ +# Duplicate Elimination Memo Table + +Note that most of the details are in `src/memo/persistent/implementation.rs`. + +For this document, we are assuming that the memo table is backed by a database / ORM. A lot of these +problems would likely not be an issue if everything was in memory. + +## Group Merging + +During logical exploration, there will be rules that create cycles between groups. The easy solution +for this is to immediately merge two groups together when the engine determines that adding an +expression would result in a duplicate expression from another group. + +However, if we want to support parallel exploration, this could be prone to high contention. By +definition, merging group G1 into group G2 would mean that _every expression_ that has a child of +group G1 with would need to be rewritten to point to group G2 instead. + +This is unacceptable in a parallel setting, as that would mean every single task that gets affected +would need to either wait for the rewrites to happen before resuming work, or need to abort their +work because data has changed underneath them. + +So immediate / eager group merging is not a great idea for parallel exploration. However, if we do +not ever merge two groups that are identical, we are subject to doing duplicate work for every +duplicate expression in the memo table during physical optimization. + +Instead of merging groups together immediately, we can instead maintain an auxiliary data structure +that records the groups that _eventually_ need to get merged, and "lazily" merge those groups +together once every group has finished exploration. + +## Union-Find Group Sets + +We use the well-known Union-Find algorithm and corresponding data structure as the auxiliary data +structure that tracks the to-be-merged groups. + +Union-Find supports `Union` and `Find` operations, where `Union` merges sets and `Find` searches for +a "canonical" or "root" element that is shared between all elements in a given set. + +For more information about Union-Find, see these +[15-451 lecture notes](https://www.cs.cmu.edu/~15451-f24/lectures/lecture08-union-find.pdf). + +Here, we make the elements the groups themselves (really the Group IDs), which allows us to merge +group sets together and also determine a "root group" that all groups in a set can agree on. + +When every group in a group set has finished exploration, we can safely begin to merge them +together by moving all expressions from every group in the group set into a single large group. +Other than making sure that any reference to an old group in the group set points to this new large +group, exploration of all groups are done and physical optimization can start. + +RFC: Do we need to support incremental search? + +Note that since we are now waiting for exploration of all groups to finish, this algorithm is much +closer to the Volcano framework than the Cascades' incremental search. However, since we eventually +will want to store trails / breadcrumbs of decisions made to skip work in the future, and since we +essentially have unlimited space due to the memo table being backed by a DBMS, this is not as much +of a problem. + +## Duplicate Detection + +TODO explain the fingerprinting algorithm and how it relates to group merging + +When taking the fingerprint of an expression, the child groups of an expression need to be root groups. If they are not, we need to try again. +Assuming that all children are root groups, the fingerprint we make for any expression that fulfills that is valid and can be looked up for duplicates. +In order to maintain that correctness, on a merge of two sets, the smaller one requires that a new fingerprint be generated for every expression that has a group in that smaller set. +For example, let's say we need to merge { 1, 2 } (root group 1) with { 3, 4, 5, 6, 7, 8 } (root group 3). We need to find every single expression that has a child group of 1 or 2 and we need to generate a new fingerprint for each where the child groups have been "rewritten" to 3 + +TODO this is incredibly expensive, but is potentially easily parallelizable? + diff --git a/optd-mvp/README.md b/optd-mvp/entities.md similarity index 100% rename from optd-mvp/README.md rename to optd-mvp/entities.md diff --git a/optd-mvp/src/entities/cascades_group.rs b/optd-mvp/src/entities/cascades_group.rs index 9c2ba83..62e1835 100644 --- a/optd-mvp/src/entities/cascades_group.rs +++ b/optd-mvp/src/entities/cascades_group.rs @@ -7,9 +7,9 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key)] pub id: i32, + pub status: i8, pub winner: Option, pub cost: Option, - pub is_optimized: bool, pub parent_id: Option, } diff --git a/optd-mvp/src/memo/mod.rs b/optd-mvp/src/memo/mod.rs index fbf23a2..83a821f 100644 --- a/optd-mvp/src/memo/mod.rs +++ b/optd-mvp/src/memo/mod.rs @@ -19,6 +19,14 @@ pub struct LogicalExpressionId(pub i32); #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct PhysicalExpressionId(pub i32); +/// A status enum representing the different states a group can be during query optimization. +#[repr(u8)] +pub enum GroupStatus { + InProgress = 0, + Explored = 1, + Optimized = 2, +} + /// The different kinds of errors that might occur while running operations on a memo table. #[derive(Error, Debug)] pub enum MemoError { diff --git a/optd-mvp/src/memo/persistent/implementation.rs b/optd-mvp/src/memo/persistent/implementation.rs index 4fc7048..002893a 100644 --- a/optd-mvp/src/memo/persistent/implementation.rs +++ b/optd-mvp/src/memo/persistent/implementation.rs @@ -10,7 +10,7 @@ use super::PersistentMemo; use crate::{ entities::*, expression::{LogicalExpression, PhysicalExpression}, - memo::{GroupId, LogicalExpressionId, MemoError, PhysicalExpressionId}, + memo::{GroupId, GroupStatus, LogicalExpressionId, MemoError, PhysicalExpressionId}, OptimizerResult, DATABASE_URL, }; use sea_orm::{ @@ -66,6 +66,40 @@ impl PersistentMemo { .ok_or(MemoError::UnknownGroup(group_id))?) } + /// Retrieves the root / canonical group ID of the given group ID. + /// + /// The groups form a union find / disjoint set parent pointer forest, where group merging + /// causes two trees to merge. + /// + /// This function uses the path compression optimization, which amortizes the cost to a single + /// lookup (theoretically in constant time, but we must be wary of the I/O roundtrip). + pub async fn get_root_group(&self, group_id: GroupId) -> OptimizerResult { + let mut curr_group = self.get_group(group_id).await?; + + // Traverse up the path and find the root group, keeping track of groups we have visited. + let mut path = vec![]; + loop { + let Some(parent_id) = curr_group.parent_id else { + break; + }; + + let next_group = self.get_group(GroupId(parent_id)).await?; + path.push(curr_group); + curr_group = next_group; + } + + let root_id = GroupId(curr_group.id); + + // Path Compression Optimization: + // For every group along the path that we walked, set their parent id pointer to the root. + // This allows for an amortized O(1) cost for `get_root_group`. + for group in path { + self.update_group_parent(GroupId(group.id), root_id).await?; + } + + Ok(root_id) + } + /// Retrieves a [`physical_expression::Model`] given a [`PhysicalExpressionId`]. /// /// If the physical expression does not exist, returns a @@ -146,6 +180,32 @@ impl PersistentMemo { Ok(children) } + /// Updates / replaces a group's status. Returns the previous group status. + /// + /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. + pub async fn update_group_status( + &self, + group_id: GroupId, + status: GroupStatus, + ) -> OptimizerResult { + // First retrieve the group record. + let mut group = self.get_group(group_id).await?.into_active_model(); + + // Update the group's status. + let old_status = group.status; + group.status = Set(status as u8 as i8); + group.update(&self.db).await?; + + let old_status = match old_status.unwrap() { + 0 => GroupStatus::InProgress, + 1 => GroupStatus::Explored, + 2 => GroupStatus::Optimized, + _ => panic!("encountered an invalid group status"), + }; + + Ok(old_status) + } + /// Updates / replaces a group's best physical plan (winner). Optionally returns the previous /// winner's physical expression ID. /// @@ -167,8 +227,32 @@ impl PersistentMemo { group.update(&self.db).await?; // Note that the `unwrap` here is unwrapping the `ActiveValue`, not the `Option`. - let old = old_id.unwrap().map(PhysicalExpressionId); - Ok(old) + let old_id = old_id.unwrap().map(PhysicalExpressionId); + Ok(old_id) + } + + /// Updates / replaces a group's parent group. Optionally returns the previous parent. + /// + /// If either of the groups do not exist, returns a [`MemoError::UnknownGroup`] error. + pub async fn update_group_parent( + &self, + group_id: GroupId, + parent_id: GroupId, + ) -> OptimizerResult> { + // First retrieve the group record. + let mut group = self.get_group(group_id).await?.into_active_model(); + + // Check that the parent group exists. + let _ = self.get_group(parent_id).await?; + + // Update the group to point to the new parent. + let old_parent = group.parent_id; + group.parent_id = Set(Some(parent_id.0)); + group.update(&self.db).await?; + + // Note that the `unwrap` here is unwrapping the `ActiveValue`, not the `Option`. + let old_parent = old_parent.unwrap().map(GroupId); + Ok(old_parent) } /// Adds a logical expression to an existing group via its ID. @@ -192,10 +276,10 @@ impl PersistentMemo { group_id: GroupId, logical_expression: LogicalExpression, children: &[GroupId], - ) -> OptimizerResult> { + ) -> OptimizerResult> { // Check if the expression already exists anywhere in the memo table. if let Some(existing_id) = self - .is_duplicate_logical_expression(&logical_expression) + .is_duplicate_logical_expression(&logical_expression, children) .await? { return Ok(Err(existing_id)); @@ -227,7 +311,15 @@ impl PersistentMemo { // Finally, insert the fingerprint of the logical expression as well. let new_expr: LogicalExpression = new_model.into(); let kind = new_expr.kind(); - let hash = new_expr.fingerprint(); + + // In order to calculate a correct fingerprint, we will want to use the IDs of the root + // groups of the children instead of the child ID themselves. + let mut rewrites = vec![]; + for &child_id in children { + let root_id = self.get_root_group(child_id).await?; + rewrites.push((child_id, root_id)); + } + let hash = new_expr.fingerprint_with_rewrite(&rewrites); let fingerprint = fingerprint::ActiveModel { id: NotSet, @@ -285,8 +377,8 @@ impl PersistentMemo { /// In order to prevent a large amount of duplicate work, the memo table must support duplicate /// expression detection. /// - /// Returns `Some(expression_id)` if the memo table detects that the expression already exists, - /// and `None` otherwise. + /// Returns `Some((group_id, expression_id))` if the memo table detects that the expression + /// already exists, and `None` otherwise. /// /// This function assumes that the child groups of the expression are currently roots of their /// group sets. For example, if G1 and G2 should be merged, and G1 is the root, then the input @@ -296,13 +388,22 @@ impl PersistentMemo { pub async fn is_duplicate_logical_expression( &self, logical_expression: &LogicalExpression, - ) -> OptimizerResult> { + children: &[GroupId], + ) -> OptimizerResult> { let model: logical_expression::Model = logical_expression.clone().into(); // Lookup all expressions that have the same fingerprint and kind. There may be false // positives, but we will check for those next. let kind = model.kind; - let fingerprint = logical_expression.fingerprint(); + + // In order to calculate a correct fingerprint, we will want to use the IDs of the root + // groups of the children instead of the child ID themselves. + let mut rewrites = vec![]; + for &child_id in children { + let root_id = self.get_root_group(child_id).await?; + rewrites.push((child_id, root_id)); + } + let fingerprint = logical_expression.fingerprint_with_rewrite(&rewrites); // Filter first by the fingerprint, and then the kind. // FIXME: The kind is already embedded into the fingerprint, so we may not actually need the @@ -323,11 +424,11 @@ impl PersistentMemo { let mut match_id = None; for potential_match in potential_matches { let expr_id = LogicalExpressionId(potential_match.logical_expression_id); - let (_, expr) = self.get_logical_expression(expr_id).await?; + let (group_id, expr) = self.get_logical_expression(expr_id).await?; // Check for an exact match. if &expr == logical_expression { - match_id = Some(expr_id); + match_id = Some((group_id, expr_id)); // There should be at most one duplicate expression, so we can break here. break; @@ -359,18 +460,17 @@ impl PersistentMemo { ) -> OptimizerResult> { // Check if the expression already exists in the memo table. - if let Some(existing_id) = self - .is_duplicate_logical_expression(&logical_expression) + if let Some((group_id, existing_id)) = self + .is_duplicate_logical_expression(&logical_expression, children) .await? { - let (group_id, _expr) = self.get_logical_expression(existing_id).await?; return Ok(Err((group_id, existing_id))); } // The expression does not exist yet, so we need to create a new group and new expression. let group = cascades_group::ActiveModel { winner: Set(None), - is_optimized: Set(false), + status: Set(0), // `GroupStatus::InProgress` status. ..Default::default() }; @@ -401,7 +501,15 @@ impl PersistentMemo { // Finally, insert the fingerprint of the logical expression as well. let new_expr: LogicalExpression = new_model.into(); let kind = new_expr.kind(); - let hash = new_expr.fingerprint(); + + // In order to calculate a correct fingerprint, we will want to use the IDs of the root + // groups of the children instead of the child ID themselves. + let mut rewrites = vec![]; + for &child_id in children { + let root_id = self.get_root_group(child_id).await?; + rewrites.push((child_id, root_id)); + } + let hash = new_expr.fingerprint_with_rewrite(&rewrites); let fingerprint = fingerprint::ActiveModel { id: NotSet, diff --git a/optd-mvp/src/memo/persistent/tests.rs b/optd-mvp/src/memo/persistent/tests.rs index f3afea6..3dcddd6 100644 --- a/optd-mvp/src/memo/persistent/tests.rs +++ b/optd-mvp/src/memo/persistent/tests.rs @@ -34,20 +34,22 @@ async fn test_simple_logical_duplicates() { // Test `add_logical_expression_to_group`. { // Attempting to add a duplicate expression into the same group should also fail every time. - let logical_expression_id_2a = memo + let (group_id_2a, logical_expression_id_2a) = memo .add_logical_expression_to_group(group_id, scan2a, &[]) .await .unwrap() .err() .unwrap(); + assert_eq!(group_id, group_id_2a); assert_eq!(logical_expression_id, logical_expression_id_2a); - let logical_expression_id_2b = memo + let (group_id_2b, logical_expression_id_2b) = memo .add_logical_expression_to_group(group_id, scan2b, &[]) .await .unwrap() .err() .unwrap(); + assert_eq!(group_id, group_id_2b); assert_eq!(logical_expression_id, logical_expression_id_2b); } @@ -140,3 +142,71 @@ async fn test_simple_tree() { memo.cleanup().await; } + +/// Tests basic group merging. See comments in the test itself for more information. +#[ignore] +#[tokio::test] +async fn test_simple_group_link() { + let memo = PersistentMemo::new().await; + memo.cleanup().await; + + // Create two scan groups. + let scan1 = scan("t1".to_string()); + let scan2 = scan("t2".to_string()); + let (scan_id_1, _) = memo.add_group(scan1, &[]).await.unwrap().ok().unwrap(); + let (scan_id_2, _) = memo.add_group(scan2, &[]).await.unwrap().ok().unwrap(); + + // Create two join expression that should be in the same group. + // Even though these are obviously the same expression (to humans), the fingerprints will be + // different, and so they will be put into different groups. + let join1 = join(scan_id_1, scan_id_2, "t1.a = t2.b".to_string()); + let join2 = join(scan_id_2, scan_id_1, "t2.b = t1.a".to_string()); + let join_unknown = join2.clone(); + + let (join_group_1, _) = memo + .add_group(join1, &[scan_id_1, scan_id_2]) + .await + .unwrap() + .ok() + .unwrap(); + let (join_group_2, join_expr_2) = memo + .add_group(join2, &[scan_id_2, scan_id_1]) + .await + .unwrap() + .ok() + .unwrap(); + assert_ne!(join_group_1, join_group_2); + + // Assume that some rule was applied to `join1`, and it outputs something like `join_unknown`. + // The memo table will tell us that `join_unknown == join2`. + // Take note here that `join_unknown` is a clone of `join2`, not `join1`. + let (existing_group, not_actually_new_expr_id) = memo + .add_logical_expression_to_group(join_group_1, join_unknown, &[scan_id_2, scan_id_1]) + .await + .unwrap() + .err() + .unwrap(); + assert_eq!(existing_group, join_group_2); + assert_eq!(not_actually_new_expr_id, join_expr_2); + + // The above tells the application that the expression already exists in the memo, specifically + // under `existing_group`. Thus, we should link these two groups together. + // Here, we arbitrarily choose to link group 1 into group 2. + memo.update_group_parent(join_group_1, join_group_2) + .await + .unwrap(); + + let test_root_1 = memo.get_root_group(join_group_1).await.unwrap(); + let test_root_2 = memo.get_root_group(join_group_2).await.unwrap(); + assert_eq!(test_root_1, test_root_2); + + // TODO(Connor) + // + // We now need to find all logical expressions that had group 1 (or whatever the root group of + // the set that group 1 belongs to is, in this case it is just group 1) as a child, and add a + // new fingerprint for each one that uses group 2 as a child instead. + // + // In order to do this, we need to iterate through every group in group 1's set. + + memo.cleanup().await; +} diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_cascades_group.rs b/optd-mvp/src/migrator/memo/m20241127_000001_cascades_group.rs index 3a0e7d0..abaa829 100644 --- a/optd-mvp/src/migrator/memo/m20241127_000001_cascades_group.rs +++ b/optd-mvp/src/migrator/memo/m20241127_000001_cascades_group.rs @@ -74,9 +74,9 @@ use sea_orm_migration::{prelude::*, schema::*}; pub enum CascadesGroup { Table, Id, + Status, Winner, Cost, - IsOptimized, ParentId, } @@ -92,8 +92,9 @@ impl MigrationTrait for Migration { .table(CascadesGroup::Table) .if_not_exists() .col(pk_auto(CascadesGroup::Id)) + .col(tiny_integer(CascadesGroup::Status)) .col(integer_null(CascadesGroup::Winner)) - .col(big_unsigned_null(CascadesGroup::Cost)) + .col(big_integer_null(CascadesGroup::Cost)) .foreign_key( ForeignKey::create() .from(CascadesGroup::Table, CascadesGroup::Winner) @@ -101,7 +102,6 @@ impl MigrationTrait for Migration { .on_delete(ForeignKeyAction::SetNull) .on_update(ForeignKeyAction::Cascade), ) - .col(boolean(CascadesGroup::IsOptimized)) .col(integer_null(CascadesGroup::ParentId)) .foreign_key( ForeignKey::create() diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_fingerprint.rs b/optd-mvp/src/migrator/memo/m20241127_000001_fingerprint.rs index 4a828b8..e153b9e 100644 --- a/optd-mvp/src/migrator/memo/m20241127_000001_fingerprint.rs +++ b/optd-mvp/src/migrator/memo/m20241127_000001_fingerprint.rs @@ -26,7 +26,7 @@ impl MigrationTrait for Migration { .table(Fingerprint::Table) .if_not_exists() .col(pk_auto(Fingerprint::Id)) - .col(unsigned(Fingerprint::LogicalExpressionId)) + .col(integer(Fingerprint::LogicalExpressionId)) .foreign_key( ForeignKey::create() .from(Fingerprint::Table, Fingerprint::LogicalExpressionId) @@ -34,8 +34,8 @@ impl MigrationTrait for Migration { .on_delete(ForeignKeyAction::Cascade) .on_update(ForeignKeyAction::Cascade), ) - .col(small_unsigned(Fingerprint::Kind)) - .col(big_unsigned(Fingerprint::Hash)) + .col(small_integer(Fingerprint::Kind)) + .col(big_integer(Fingerprint::Hash)) .to_owned(), ) .await From 194ae5e210c12359c7648917306ea11be9f9d8a6 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Sun, 1 Dec 2024 13:20:02 -0500 Subject: [PATCH 08/10] rename cascades_group to group --- optd-mvp/DESIGN.md | 2 + .../entities/{cascades_group.rs => group.rs} | 21 +++--- optd-mvp/src/entities/logical_children.rs | 10 +-- optd-mvp/src/entities/logical_expression.rs | 14 ++-- optd-mvp/src/entities/mod.rs | 2 +- optd-mvp/src/entities/physical_children.rs | 10 +-- optd-mvp/src/entities/physical_expression.rs | 10 +-- optd-mvp/src/entities/prelude.rs | 2 +- optd-mvp/src/expression/logical_expression.rs | 2 +- optd-mvp/src/expression/mod.rs | 4 +- .../src/expression/physical_expression.rs | 2 +- optd-mvp/src/memo/mod.rs | 3 +- .../src/memo/persistent/implementation.rs | 42 ++++++------ ...des_group.rs => m20241127_000001_group.rs} | 67 ++++++++++++------- .../memo/m20241127_000001_logical_children.rs | 12 ++-- .../m20241127_000001_logical_expression.rs | 16 ++--- .../m20241127_000001_physical_children.rs | 14 ++-- .../m20241127_000001_physical_expression.rs | 16 ++--- optd-mvp/src/migrator/memo/mod.rs | 8 +-- optd-mvp/src/migrator/mod.rs | 2 +- 20 files changed, 137 insertions(+), 122 deletions(-) rename optd-mvp/src/entities/{cascades_group.rs => group.rs} (82%) rename optd-mvp/src/migrator/memo/{m20241127_000001_cascades_group.rs => m20241127_000001_group.rs} (61%) diff --git a/optd-mvp/DESIGN.md b/optd-mvp/DESIGN.md index e50a56c..190eee3 100644 --- a/optd-mvp/DESIGN.md +++ b/optd-mvp/DESIGN.md @@ -58,6 +58,8 @@ of a problem. TODO explain the fingerprinting algorithm and how it relates to group merging +Union find data structure with a circular linked list for linear iteration + When taking the fingerprint of an expression, the child groups of an expression need to be root groups. If they are not, we need to try again. Assuming that all children are root groups, the fingerprint we make for any expression that fulfills that is valid and can be looked up for duplicates. In order to maintain that correctness, on a merge of two sets, the smaller one requires that a new fingerprint be generated for every expression that has a group in that smaller set. diff --git a/optd-mvp/src/entities/cascades_group.rs b/optd-mvp/src/entities/group.rs similarity index 82% rename from optd-mvp/src/entities/cascades_group.rs rename to optd-mvp/src/entities/group.rs index 62e1835..333ab05 100644 --- a/optd-mvp/src/entities/cascades_group.rs +++ b/optd-mvp/src/entities/group.rs @@ -3,7 +3,7 @@ use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] -#[sea_orm(table_name = "cascades_group")] +#[sea_orm(table_name = "group")] pub struct Model { #[sea_orm(primary_key)] pub id: i32, @@ -11,10 +11,19 @@ pub struct Model { pub winner: Option, pub cost: Option, pub parent_id: Option, + pub next_id: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { + #[sea_orm( + belongs_to = "Entity", + from = "Column::NextId", + to = "Column::Id", + on_update = "Cascade", + on_delete = "SetNull" + )] + SelfRef2, #[sea_orm( belongs_to = "Entity", from = "Column::ParentId", @@ -22,7 +31,7 @@ pub enum Relation { on_update = "Cascade", on_delete = "SetNull" )] - SelfRef, + SelfRef1, #[sea_orm(has_many = "super::logical_children::Entity")] LogicalChildren, #[sea_orm(has_many = "super::logical_expression::Entity")] @@ -56,7 +65,7 @@ impl Related for Entity { super::logical_children::Relation::LogicalExpression.def() } fn via() -> Option { - Some(super::logical_children::Relation::CascadesGroup.def().rev()) + Some(super::logical_children::Relation::Group.def().rev()) } } @@ -65,11 +74,7 @@ impl Related for Entity { super::physical_children::Relation::PhysicalExpression.def() } fn via() -> Option { - Some( - super::physical_children::Relation::CascadesGroup - .def() - .rev(), - ) + Some(super::physical_children::Relation::Group.def().rev()) } } diff --git a/optd-mvp/src/entities/logical_children.rs b/optd-mvp/src/entities/logical_children.rs index 067eaa7..a0ac39c 100644 --- a/optd-mvp/src/entities/logical_children.rs +++ b/optd-mvp/src/entities/logical_children.rs @@ -14,13 +14,13 @@ pub struct Model { #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { #[sea_orm( - belongs_to = "super::cascades_group::Entity", + belongs_to = "super::group::Entity", from = "Column::GroupId", - to = "super::cascades_group::Column::Id", + to = "super::group::Column::Id", on_update = "Cascade", on_delete = "Cascade" )] - CascadesGroup, + Group, #[sea_orm( belongs_to = "super::logical_expression::Entity", from = "Column::LogicalExpressionId", @@ -31,9 +31,9 @@ pub enum Relation { LogicalExpression, } -impl Related for Entity { +impl Related for Entity { fn to() -> RelationDef { - Relation::CascadesGroup.def() + Relation::Group.def() } } diff --git a/optd-mvp/src/entities/logical_expression.rs b/optd-mvp/src/entities/logical_expression.rs index 4c257f3..82d938f 100644 --- a/optd-mvp/src/entities/logical_expression.rs +++ b/optd-mvp/src/entities/logical_expression.rs @@ -14,16 +14,16 @@ pub struct Model { #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { + #[sea_orm(has_many = "super::fingerprint::Entity")] + Fingerprint, #[sea_orm( - belongs_to = "super::cascades_group::Entity", + belongs_to = "super::group::Entity", from = "Column::GroupId", - to = "super::cascades_group::Column::Id", + to = "super::group::Column::Id", on_update = "Cascade", on_delete = "Cascade" )] - CascadesGroup, - #[sea_orm(has_many = "super::fingerprint::Entity")] - Fingerprint, + Group, #[sea_orm(has_many = "super::logical_children::Entity")] LogicalChildren, } @@ -40,9 +40,9 @@ impl Related for Entity { } } -impl Related for Entity { +impl Related for Entity { fn to() -> RelationDef { - super::logical_children::Relation::CascadesGroup.def() + super::logical_children::Relation::Group.def() } fn via() -> Option { Some( diff --git a/optd-mvp/src/entities/mod.rs b/optd-mvp/src/entities/mod.rs index 77d6b2c..3abd379 100644 --- a/optd-mvp/src/entities/mod.rs +++ b/optd-mvp/src/entities/mod.rs @@ -2,8 +2,8 @@ pub mod prelude; -pub mod cascades_group; pub mod fingerprint; +pub mod group; pub mod logical_children; pub mod logical_expression; pub mod physical_children; diff --git a/optd-mvp/src/entities/physical_children.rs b/optd-mvp/src/entities/physical_children.rs index d8f9db0..e58e9ca 100644 --- a/optd-mvp/src/entities/physical_children.rs +++ b/optd-mvp/src/entities/physical_children.rs @@ -14,13 +14,13 @@ pub struct Model { #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { #[sea_orm( - belongs_to = "super::cascades_group::Entity", + belongs_to = "super::group::Entity", from = "Column::GroupId", - to = "super::cascades_group::Column::Id", + to = "super::group::Column::Id", on_update = "Cascade", on_delete = "Cascade" )] - CascadesGroup, + Group, #[sea_orm( belongs_to = "super::physical_expression::Entity", from = "Column::PhysicalExpressionId", @@ -31,9 +31,9 @@ pub enum Relation { PhysicalExpression, } -impl Related for Entity { +impl Related for Entity { fn to() -> RelationDef { - Relation::CascadesGroup.def() + Relation::Group.def() } } diff --git a/optd-mvp/src/entities/physical_expression.rs b/optd-mvp/src/entities/physical_expression.rs index 482227a..4fba71e 100644 --- a/optd-mvp/src/entities/physical_expression.rs +++ b/optd-mvp/src/entities/physical_expression.rs @@ -15,13 +15,13 @@ pub struct Model { #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { #[sea_orm( - belongs_to = "super::cascades_group::Entity", + belongs_to = "super::group::Entity", from = "Column::GroupId", - to = "super::cascades_group::Column::Id", + to = "super::group::Column::Id", on_update = "Cascade", on_delete = "Cascade" )] - CascadesGroup, + Group, #[sea_orm(has_many = "super::physical_children::Entity")] PhysicalChildren, } @@ -32,9 +32,9 @@ impl Related for Entity { } } -impl Related for Entity { +impl Related for Entity { fn to() -> RelationDef { - super::physical_children::Relation::CascadesGroup.def() + super::physical_children::Relation::Group.def() } fn via() -> Option { Some( diff --git a/optd-mvp/src/entities/prelude.rs b/optd-mvp/src/entities/prelude.rs index bf6879b..8e8deaa 100644 --- a/optd-mvp/src/entities/prelude.rs +++ b/optd-mvp/src/entities/prelude.rs @@ -2,8 +2,8 @@ #![allow(unused_imports)] -pub use super::cascades_group::Entity as CascadesGroup; pub use super::fingerprint::Entity as Fingerprint; +pub use super::group::Entity as Group; pub use super::logical_children::Entity as LogicalChildren; pub use super::logical_expression::Entity as LogicalExpression; pub use super::physical_children::Entity as PhysicalChildren; diff --git a/optd-mvp/src/expression/logical_expression.rs b/optd-mvp/src/expression/logical_expression.rs index 7c3362d..c7918de 100644 --- a/optd-mvp/src/expression/logical_expression.rs +++ b/optd-mvp/src/expression/logical_expression.rs @@ -1,4 +1,4 @@ -//! Definition of logical expressions / relations in the Cascades query optimization framework. +//! Definition of logical expressions / relations in our query optimization framework. //! //! FIXME: All fields are placeholders. //! diff --git a/optd-mvp/src/expression/mod.rs b/optd-mvp/src/expression/mod.rs index 459e13b..3b6d7cf 100644 --- a/optd-mvp/src/expression/mod.rs +++ b/optd-mvp/src/expression/mod.rs @@ -1,4 +1,4 @@ -//! In-memory representation of Cascades logical and physical expression / operators / relations. +//! In-memory representation of logical and physical expression / operators / relations. //! //! TODO more docs. @@ -8,7 +8,7 @@ pub use logical_expression::*; mod physical_expression; pub use physical_expression::*; -/// The representation of a Cascades expression. +/// The representation of an expression. /// /// TODO more docs. #[derive(Clone, Debug)] diff --git a/optd-mvp/src/expression/physical_expression.rs b/optd-mvp/src/expression/physical_expression.rs index 5719752..aaaa9e7 100644 --- a/optd-mvp/src/expression/physical_expression.rs +++ b/optd-mvp/src/expression/physical_expression.rs @@ -1,4 +1,4 @@ -//! Definition of physical expressions / operators in the Cascades query optimization framework. +//! Definition of physical expressions / operators in our query optimization framework. //! //! FIXME: All fields are placeholders. //! diff --git a/optd-mvp/src/memo/mod.rs b/optd-mvp/src/memo/mod.rs index 83a821f..08b74db 100644 --- a/optd-mvp/src/memo/mod.rs +++ b/optd-mvp/src/memo/mod.rs @@ -1,5 +1,4 @@ -//! This module contains items related to the memo table, which is key to the Cascades query -//! optimization framework. +//! This module contains items related to the memo table. //! //! TODO more docs. diff --git a/optd-mvp/src/memo/persistent/implementation.rs b/optd-mvp/src/memo/persistent/implementation.rs index 002893a..d7e7c25 100644 --- a/optd-mvp/src/memo/persistent/implementation.rs +++ b/optd-mvp/src/memo/persistent/implementation.rs @@ -45,7 +45,7 @@ impl PersistentMemo { } delete_all! { - cascades_group, + group, fingerprint, logical_expression, logical_children, @@ -54,13 +54,13 @@ impl PersistentMemo { }; } - /// Retrieves a [`cascades_group::Model`] given its ID. + /// Retrieves a [`group::Model`] given its ID. /// /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. /// /// FIXME: use an in-memory representation of a group instead. - pub async fn get_group(&self, group_id: GroupId) -> OptimizerResult { - Ok(cascades_group::Entity::find_by_id(group_id.0) + pub async fn get_group(&self, group_id: GroupId) -> OptimizerResult { + Ok(group::Entity::find_by_id(group_id.0) .one(&self.db) .await? .ok_or(MemoError::UnknownGroup(group_id))?) @@ -78,11 +78,7 @@ impl PersistentMemo { // Traverse up the path and find the root group, keeping track of groups we have visited. let mut path = vec![]; - loop { - let Some(parent_id) = curr_group.parent_id else { - break; - }; - + while let Some(parent_id) = curr_group.parent_id { let next_group = self.get_group(GroupId(parent_id)).await?; path.push(curr_group); curr_group = next_group; @@ -468,29 +464,29 @@ impl PersistentMemo { } // The expression does not exist yet, so we need to create a new group and new expression. - let group = cascades_group::ActiveModel { - winner: Set(None), + let group = group::ActiveModel { status: Set(0), // `GroupStatus::InProgress` status. ..Default::default() }; // Create the new group. - let res = cascades_group::Entity::insert(group).exec(&self.db).await?; + let group_res = group::Entity::insert(group).exec(&self.db).await?; + let group_id = group_res.last_insert_id; // Insert the input expression into the newly created group. - let model: logical_expression::Model = logical_expression.clone().into(); - let mut active_model = model.into_active_model(); - active_model.group_id = Set(res.last_insert_id); - active_model.id = NotSet; - let new_model = active_model.insert(&self.db).await?; + let expression: logical_expression::Model = logical_expression.clone().into(); + let mut active_expression = expression.into_active_model(); + active_expression.group_id = Set(group_id); + active_expression.id = NotSet; + let new_expression = active_expression.insert(&self.db).await?; - let group_id = new_model.group_id; - let expr_id = new_model.id; + let group_id = new_expression.group_id; + let expr_id = new_expression.id; // Insert the child groups of the expression into the junction / children table. logical_children::Entity::insert_many(children.iter().copied().map(|child_id| { logical_children::ActiveModel { - logical_expression_id: Set(new_model.id), + logical_expression_id: Set(new_expression.id), group_id: Set(child_id.0), } })) @@ -499,8 +495,8 @@ impl PersistentMemo { .await?; // Finally, insert the fingerprint of the logical expression as well. - let new_expr: LogicalExpression = new_model.into(); - let kind = new_expr.kind(); + let new_logical_expression: LogicalExpression = new_expression.into(); + let kind = new_logical_expression.kind(); // In order to calculate a correct fingerprint, we will want to use the IDs of the root // groups of the children instead of the child ID themselves. @@ -509,7 +505,7 @@ impl PersistentMemo { let root_id = self.get_root_group(child_id).await?; rewrites.push((child_id, root_id)); } - let hash = new_expr.fingerprint_with_rewrite(&rewrites); + let hash = new_logical_expression.fingerprint_with_rewrite(&rewrites); let fingerprint = fingerprint::ActiveModel { id: NotSet, diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_cascades_group.rs b/optd-mvp/src/migrator/memo/m20241127_000001_group.rs similarity index 61% rename from optd-mvp/src/migrator/memo/m20241127_000001_cascades_group.rs rename to optd-mvp/src/migrator/memo/m20241127_000001_group.rs index abaa829..d5bbe0e 100644 --- a/optd-mvp/src/migrator/memo/m20241127_000001_cascades_group.rs +++ b/optd-mvp/src/migrator/memo/m20241127_000001_group.rs @@ -1,3 +1,5 @@ +//! FIXME We aren't really following the cascades framework anymore... +//! //! An entity representing a group / equivalence class in the Cascades framework. //! //! Quoted from the Microsoft article _Extensible query optimizers in practice_: @@ -17,38 +19,42 @@ //! # Columns //! //! Each group is assigned a monotonically-increasing (unique) ID. This ID will be important since -//! there are many foreign key references from other tables to `cascades_group`. +//! there are many foreign key references from other tables to `group`. +//! +//! We store an `status` enumeration encoded as an 8-bit integer that is used for quickly +//! determining the state of optimization for this group during the dynamic programming search. //! -//! We additionally store a `latest_winner` foreign key reference to a physical expression. See -//! the [section](#best-physical-plan-winner) below for more details. +//! We additionally store a `winner` foreign key reference to a physical expression paired with a +//! `cost` foreign key reference to a cost record (FIXME). See the +//! [section](#best-physical-plan-winner) below for more details. //! -//! Finally, we store an `is_optimized` flag that is used for quickly determining the state of -//! optimization for this group during the dynamic programming search. +//! Finally, we maintain a union-find graph structure embedded in the group records. +//! TODO write more information about this once this is implemented. //! //! # Entity Relationships //! //! ### Child Expressions (Logical and Physical) //! -//! To retrieve all of a `cascades_group`'s equivalent expressions, you must query the +//! To retrieve all of a `group`'s equivalent expressions, you must query the //! [`logical_expression`] or the [`physical_expression`] entities via their foreign keys to -//! `cascades_group`. The relationship between [`logical_expression`] and `cascades_group` is +//! `group`. The relationship between [`logical_expression`] and `group` is //! many-to-one, and the exact same many-to-one relationship is held for [`physical_expression`] to -//! `cascades_group`. +//! `group`. //! //! ### Parent Expressions (Logical and Physical) //! -//! Additionally, each logical or physical expression can have any number of `cascades_group`s as -//! children, and a group can be a child of any expression. Thus, `cascades_group` additionally has +//! Additionally, each logical or physical expression can have any number of `group`s as +//! children, and a group can be a child of any expression. Thus, `group` additionally has //! a many-to-many relationship with [`logical_expression`] and [`physical_expression`] via the //! [`logical_children`] and [`physical_children`] entities. //! -//! To reiterate, `cascades_group` has **both** a one-to-many **and** a many-to-many relationship +//! To reiterate, `group` has **both** a one-to-many **and** a many-to-many relationship //! with both [`logical_expression`] and [`physical_expression`]. This is due to groups being both //! parents and children of expressions. //! //! ### Best Physical Plan (Winner) //! -//! The `cascades_group` entity also stores a `latest_winner` _nullable_ foreign key reference to +//! The `group` entity also stores a `winner` _nullable_ foreign key reference to //! a physical expression. This represents the most recent best query plan we have computed. The //! reason it is nullable is because we may not have come up with any best query plan yet. //! @@ -56,10 +62,10 @@ //! //! FIXME: Add a logical properties table. //! -//! Lastly, each `cascades_group` record will have a set of logical properties store in the +//! Lastly, each `group` record will have a set of logical properties store in the //! `logical_property` entity, where there is an many-to-one relationship from -//! `logical_property` to `cascades_group`. Note that we do not store physical properties directly -//! on the `cascades_group`, but rather we store them for each [`physical_expression`] record. +//! `logical_property` to `group`. Note that we do not store physical properties directly +//! on the `group`, but rather we store them for each [`physical_expression`] record. //! //! [`logical_expression`]: super::logical_expression //! [`physical_expression`]: super::physical_expression @@ -71,13 +77,14 @@ use crate::migrator::memo::physical_expression::PhysicalExpression; use sea_orm_migration::{prelude::*, schema::*}; #[derive(DeriveIden)] -pub enum CascadesGroup { +pub enum Group { Table, Id, Status, Winner, Cost, ParentId, + NextId, } #[derive(DeriveMigrationName)] @@ -89,24 +96,32 @@ impl MigrationTrait for Migration { manager .create_table( Table::create() - .table(CascadesGroup::Table) + .table(Group::Table) .if_not_exists() - .col(pk_auto(CascadesGroup::Id)) - .col(tiny_integer(CascadesGroup::Status)) - .col(integer_null(CascadesGroup::Winner)) - .col(big_integer_null(CascadesGroup::Cost)) + .col(pk_auto(Group::Id)) + .col(tiny_integer(Group::Status)) + .col(integer_null(Group::Winner)) + .col(big_integer_null(Group::Cost)) .foreign_key( ForeignKey::create() - .from(CascadesGroup::Table, CascadesGroup::Winner) + .from(Group::Table, Group::Winner) .to(PhysicalExpression::Table, PhysicalExpression::Id) .on_delete(ForeignKeyAction::SetNull) .on_update(ForeignKeyAction::Cascade), ) - .col(integer_null(CascadesGroup::ParentId)) + .col(integer_null(Group::ParentId)) + .foreign_key( + ForeignKey::create() + .from(Group::Table, Group::ParentId) + .to(Group::Table, Group::Id) + .on_delete(ForeignKeyAction::SetNull) + .on_update(ForeignKeyAction::Cascade), + ) + .col(integer_null(Group::NextId)) .foreign_key( ForeignKey::create() - .from(CascadesGroup::Table, CascadesGroup::ParentId) - .to(CascadesGroup::Table, CascadesGroup::Id) + .from(Group::Table, Group::NextId) + .to(Group::Table, Group::Id) .on_delete(ForeignKeyAction::SetNull) .on_update(ForeignKeyAction::Cascade), ) @@ -117,7 +132,7 @@ impl MigrationTrait for Migration { async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { manager - .drop_table(Table::drop().table(CascadesGroup::Table).to_owned()) + .drop_table(Table::drop().table(Group::Table).to_owned()) .await } } diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_logical_children.rs b/optd-mvp/src/migrator/memo/m20241127_000001_logical_children.rs index 037a637..12b5aa6 100644 --- a/optd-mvp/src/migrator/memo/m20241127_000001_logical_children.rs +++ b/optd-mvp/src/migrator/memo/m20241127_000001_logical_children.rs @@ -1,16 +1,16 @@ -//! An entity representing the [`cascades_group`] children of every [`logical_expression`]. +//! An entity representing the [`group`] children of every [`logical_expression`]. //! //! Formally, this entity is a junction which allows us to represent a many-to-many relationship -//! between [`logical_expression`] and [`cascades_group`]. Expressions can have any number of child +//! between [`logical_expression`] and [`group`]. Expressions can have any number of child //! groups, and every group can be a child of many different expressions, hence the many-to-many //! relationship. //! -//! See [`cascades_group`] for more details. +//! See [`group`] for more details. //! -//! [`cascades_group`]: super::cascades_group +//! [`group`]: super::group //! [`logical_expression`]: super::logical_expression -use crate::migrator::memo::{cascades_group::CascadesGroup, logical_expression::LogicalExpression}; +use crate::migrator::memo::{group::Group, logical_expression::LogicalExpression}; use sea_orm_migration::{prelude::*, schema::*}; #[derive(DeriveIden)] @@ -48,7 +48,7 @@ impl MigrationTrait for Migration { .foreign_key( ForeignKey::create() .from(LogicalChildren::Table, LogicalChildren::GroupId) - .to(CascadesGroup::Table, CascadesGroup::Id) + .to(Group::Table, Group::Id) .on_delete(ForeignKeyAction::Cascade) .on_update(ForeignKeyAction::Cascade), ) diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_logical_expression.rs b/optd-mvp/src/migrator/memo/m20241127_000001_logical_expression.rs index 57356cf..9b5eefc 100644 --- a/optd-mvp/src/migrator/memo/m20241127_000001_logical_expression.rs +++ b/optd-mvp/src/migrator/memo/m20241127_000001_logical_expression.rs @@ -1,4 +1,4 @@ -//! An entity representing a logical plan expression in the Cascades framework. +//! An entity representing a logical relational expression. //! //! Quoted from the Microsoft article _Extensible query optimizers in practice_: //! @@ -6,7 +6,7 @@ //! > relational algebraic expression. //! //! In the Cascades query optimization framework, the memo table stores equivalence classes of -//! expressions (see [`cascades_group`]). These equivalence classes, or "groups", store both +//! expressions (see [`group`]). These equivalence classes, or "groups", store both //! `logical_expression`s and [`physical_expression`]s. //! //! Optimization starts by "exploring" equivalent logical expressions within a group. For example, @@ -24,20 +24,20 @@ //! //! # Entity Relationships //! -//! The main relationship that `logical_expression` has is to [`cascades_group`]. It has **both** a -//! one-to-many **and** a many-to-many relationship with [`cascades_group`], and you can see more -//! details about this in the module-level documentation for [`cascades_group`]. +//! The main relationship that `logical_expression` has is to [`group`]. It has **both** a +//! one-to-many **and** a many-to-many relationship with [`group`], and you can see more +//! details about this in the module-level documentation for [`group`]. //! //! The other relationship that `logical_expression` has is to [`fingerprint`]. This table stores //! 1 or more fingerprints for every (unique) logical expression. The reason we have multiple //! fingerprints is that an expression can belong to multiple groups during the exploration phase //! before the merging of groups. //! -//! [`cascades_group`]: super::cascades_group +//! [`group`]: super::group //! [`physical_expression`]: super::physical_expression //! [`fingerprint`]: super::fingerprint -use crate::migrator::memo::cascades_group::CascadesGroup; +use crate::migrator::memo::group::Group; use sea_orm_migration::{prelude::*, schema::*}; #[derive(DeriveIden)] @@ -65,7 +65,7 @@ impl MigrationTrait for Migration { .foreign_key( ForeignKey::create() .from(LogicalExpression::Table, LogicalExpression::GroupId) - .to(CascadesGroup::Table, CascadesGroup::Id) + .to(Group::Table, Group::Id) .on_delete(ForeignKeyAction::Cascade) .on_update(ForeignKeyAction::Cascade), ) diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_physical_children.rs b/optd-mvp/src/migrator/memo/m20241127_000001_physical_children.rs index 3983f0c..06ce259 100644 --- a/optd-mvp/src/migrator/memo/m20241127_000001_physical_children.rs +++ b/optd-mvp/src/migrator/memo/m20241127_000001_physical_children.rs @@ -1,18 +1,16 @@ -//! An entity representing the [`cascades_group`] children of every [`physical_expression`]. +//! An entity representing the [`group`] children of every [`physical_expression`]. //! //! Formally, this entity is a junction which allows us to represent a many-to-many relationship -//! between [`physical_expression`] and [`cascades_group`]. Expressions can have any number of child +//! between [`physical_expression`] and [`group`]. Expressions can have any number of child //! groups, and every group can be a child of many different expressions, hence the many-to-many //! relationship. //! -//! See [`cascades_group`] for more details. +//! See [`group`] for more details. //! -//! [`cascades_group`]: super::cascades_group +//! [`group`]: super::group //! [`physical_expression`]: super::physical_expression -use crate::migrator::memo::{ - cascades_group::CascadesGroup, physical_expression::PhysicalExpression, -}; +use crate::migrator::memo::{group::Group, physical_expression::PhysicalExpression}; use sea_orm_migration::{prelude::*, schema::*}; #[derive(DeriveIden)] @@ -53,7 +51,7 @@ impl MigrationTrait for Migration { .foreign_key( ForeignKey::create() .from(PhysicalChildren::Table, PhysicalChildren::GroupId) - .to(CascadesGroup::Table, CascadesGroup::Id) + .to(Group::Table, Group::Id) .on_delete(ForeignKeyAction::Cascade) .on_update(ForeignKeyAction::Cascade), ) diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_physical_expression.rs b/optd-mvp/src/migrator/memo/m20241127_000001_physical_expression.rs index 1e66195..88cd63b 100644 --- a/optd-mvp/src/migrator/memo/m20241127_000001_physical_expression.rs +++ b/optd-mvp/src/migrator/memo/m20241127_000001_physical_expression.rs @@ -1,4 +1,4 @@ -//! An entity representing a logical plan expression in the Cascades framework. +//! An entity representing a physical plan expression. //! //! Quoted from the Microsoft article _Extensible query optimizers in practice_: //! @@ -6,7 +6,7 @@ //! > _physical plan_ or simply _plan_. //! //! In the Cascades query optimization framework, the memo table stores equivalence classes of -//! expressions (see [`cascades_group`]). These equivalence classes, or "groups", store both +//! expressions (see [`group`]). These equivalence classes, or "groups", store both //! [`logical_expression`]s and `physical_expression`s. //! //! Optimization starts by exploring equivalent logical expressions within a group, and then it @@ -30,14 +30,14 @@ //! //! # Entity Relationships //! -//! The only relationship that `physical_expression` has is to [`cascades_group`]. It has **both** a -//! one-to-many **and** a many-to-many relationship with [`cascades_group`], and you can see more -//! details about this in the module-level documentation for [`cascades_group`]. +//! The only relationship that `physical_expression` has is to [`group`]. It has **both** a +//! one-to-many **and** a many-to-many relationship with [`group`], and you can see more +//! details about this in the module-level documentation for [`group`]. //! -//! [`cascades_group`]: super::cascades_group +//! [`group`]: super::group //! [`logical_expression`]: super::logical_expression -use crate::migrator::memo::cascades_group::CascadesGroup; +use crate::migrator::memo::group::Group; use sea_orm_migration::{prelude::*, schema::*}; #[derive(DeriveIden)] @@ -65,7 +65,7 @@ impl MigrationTrait for Migration { .foreign_key( ForeignKey::create() .from(PhysicalExpression::Table, PhysicalExpression::GroupId) - .to(CascadesGroup::Table, CascadesGroup::Id) + .to(Group::Table, Group::Id) .on_delete(ForeignKeyAction::Cascade) .on_update(ForeignKeyAction::Cascade), ) diff --git a/optd-mvp/src/migrator/memo/mod.rs b/optd-mvp/src/migrator/memo/mod.rs index 7a60c9b..a419189 100644 --- a/optd-mvp/src/migrator/memo/mod.rs +++ b/optd-mvp/src/migrator/memo/mod.rs @@ -1,15 +1,15 @@ -//! Entities related to the memo table used for dynamic programming in the Cascades query -//! optimization framework. +//! Entities related to the memo table used for dynamic programming in the our query optimization +//! framework. -pub(crate) mod m20241127_000001_cascades_group; pub(crate) mod m20241127_000001_fingerprint; +pub(crate) mod m20241127_000001_group; pub(crate) mod m20241127_000001_logical_children; pub(crate) mod m20241127_000001_logical_expression; pub(crate) mod m20241127_000001_physical_children; pub(crate) mod m20241127_000001_physical_expression; -pub(crate) use m20241127_000001_cascades_group as cascades_group; pub(crate) use m20241127_000001_fingerprint as fingerprint; +pub(crate) use m20241127_000001_group as group; pub(crate) use m20241127_000001_logical_children as logical_children; pub(crate) use m20241127_000001_logical_expression as logical_expression; pub(crate) use m20241127_000001_physical_children as physical_children; diff --git a/optd-mvp/src/migrator/mod.rs b/optd-mvp/src/migrator/mod.rs index 0945423..cbc39ae 100644 --- a/optd-mvp/src/migrator/mod.rs +++ b/optd-mvp/src/migrator/mod.rs @@ -8,7 +8,7 @@ pub struct Migrator; impl MigratorTrait for Migrator { fn migrations() -> Vec> { vec![ - Box::new(memo::cascades_group::Migration), + Box::new(memo::group::Migration), Box::new(memo::fingerprint::Migration), Box::new(memo::logical_expression::Migration), Box::new(memo::logical_children::Migration), From e075d8f89d6343caa4c5394dae581dd1ec20c834 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Sun, 1 Dec 2024 14:32:57 -0500 Subject: [PATCH 09/10] first draft fully merge group --- optd-mvp/DESIGN.md | 191 ++++++++++-- optd-mvp/entities.md | 2 +- optd-mvp/src/expression/logical_expression.rs | 76 +++-- .../src/expression/physical_expression.rs | 26 +- optd-mvp/src/lib.rs | 2 - .../src/memo/persistent/implementation.rs | 183 ++++++++--- optd-mvp/src/memo/persistent/tests.rs | 287 +++++++++++++++++- .../migrator/memo/m20241127_000001_group.rs | 4 +- 8 files changed, 650 insertions(+), 121 deletions(-) diff --git a/optd-mvp/DESIGN.md b/optd-mvp/DESIGN.md index 190eee3..6d1863d 100644 --- a/optd-mvp/DESIGN.md +++ b/optd-mvp/DESIGN.md @@ -1,9 +1,12 @@ # Duplicate Elimination Memo Table +_Connor Tsui, December 2024_ + Note that most of the details are in `src/memo/persistent/implementation.rs`. -For this document, we are assuming that the memo table is backed by a database / ORM. A lot of these -problems would likely not be an issue if everything was in memory. +For this document, we are assuming that the memo table is backed by a database / ORM. Both the +problems and the features detailed in this document are unique to this design, and likely do not +apply to an in-memory memo table. ## Group Merging @@ -12,20 +15,21 @@ for this is to immediately merge two groups together when the engine determines expression would result in a duplicate expression from another group. However, if we want to support parallel exploration, this could be prone to high contention. By -definition, merging group G1 into group G2 would mean that _every expression_ that has a child of -group G1 with would need to be rewritten to point to group G2 instead. +definition, merging group 1 into group 2 would mean that _every expression_ that has a child of +group 1 with would need to be rewritten to point to group 2 instead. -This is unacceptable in a parallel setting, as that would mean every single task that gets affected -would need to either wait for the rewrites to happen before resuming work, or need to abort their -work because data has changed underneath them. +This is prohibitive in a parallel setting, as that would mean every single task that gets affected +would need to either wait for the rewrites to happen before resuming work, or potentially need to +abort their work because data has changed underneath them. -So immediate / eager group merging is not a great idea for parallel exploration. However, if we do -not ever merge two groups that are identical, we are subject to doing duplicate work for every +So immediate / eager group merging is not a great idea for parallel exploration. However, if we +don't merge two groups that are equivalent, we are subject to doing duplicate work for every duplicate expression in the memo table during physical optimization. Instead of merging groups together immediately, we can instead maintain an auxiliary data structure that records the groups that _eventually_ need to get merged, and "lazily" merge those groups -together once every group has finished exploration. +together once every group has finished exploration. We will refer to merging groups as the act of +recording that the groups should eventually be merged together after exploration is finished. ## Union-Find Group Sets @@ -33,20 +37,22 @@ We use the well-known Union-Find algorithm and corresponding data structure as t structure that tracks the to-be-merged groups. Union-Find supports `Union` and `Find` operations, where `Union` merges sets and `Find` searches for -a "canonical" or "root" element that is shared between all elements in a given set. +a "canonical" or "root" element that is shared between all elements in a given set. Note that we +will also support an iteration operation that iterates over all elements in a given set. We will +need this for [duplicate detection](#fingerprinting--group-merge), which is explained below. For more information about Union-Find, see these -[15-451 lecture notes](https://www.cs.cmu.edu/~15451-f24/lectures/lecture08-union-find.pdf). +[15-451 lecture notes](https://www.cs.cmu.edu/~15451-f24/lectures/lecture08-union-find.pdf). We will +use the exact same data structure, but add an additional `next` pointer for each node that embeds +a circular linked list for each set. -Here, we make the elements the groups themselves (really the Group IDs), which allows us to merge +Here, we make the elements the groups themselves (really the group IDs), which allows us to merge group sets together and also determine a "root group" that all groups in a set can agree on. When every group in a group set has finished exploration, we can safely begin to merge them together by moving all expressions from every group in the group set into a single large group. Other than making sure that any reference to an old group in the group set points to this new large -group, exploration of all groups are done and physical optimization can start. - -RFC: Do we need to support incremental search? +group, exploration of all groups is done and physical optimization can start. Note that since we are now waiting for exploration of all groups to finish, this algorithm is much closer to the Volcano framework than the Cascades' incremental search. However, since we eventually @@ -56,14 +62,153 @@ of a problem. ## Duplicate Detection -TODO explain the fingerprinting algorithm and how it relates to group merging +Deciding that we will merge groups lazily does not solve all of our problems. We have to know _when_ +we want to merge these groups. -Union find data structure with a circular linked list for linear iteration +A naive approach is to simply loop over every expression in the memo table and check if we are about +to insert a duplicate. This, of course, is bad for performance. -When taking the fingerprint of an expression, the child groups of an expression need to be root groups. If they are not, we need to try again. -Assuming that all children are root groups, the fingerprint we make for any expression that fulfills that is valid and can be looked up for duplicates. -In order to maintain that correctness, on a merge of two sets, the smaller one requires that a new fingerprint be generated for every expression that has a group in that smaller set. -For example, let's say we need to merge { 1, 2 } (root group 1) with { 3, 4, 5, 6, 7, 8 } (root group 3). We need to find every single expression that has a child group of 1 or 2 and we need to generate a new fingerprint for each where the child groups have been "rewritten" to 3 +We will use a fingerprinting / hashing method to detect when a duplicate expression might be +inserted into the memo table (returning an error instead of inserting), and we will use that to +trigger group merges. -TODO this is incredibly expensive, but is potentially easily parallelizable? +For every logical expression we insert into the memo table, we will create a fingerprint that +contains both the kind of expression / relation (Scan, Filter, Join) and a hash of all +information that makes that expression unique. For example: +- The fingerprint of a Scan should probably contain a hash of the table name and the pushdown + predicate. +- The fingerprint of a Filter should probably contain a hash of its child group ID and predicate. +- The fingerprint of a Join should probably contain a hash of the left group ID and the right group + ID, as well as the join predicate. + +Note that the above descriptions are slightly inaccurate, and we'll explain why in a later +[section](#fingerprinting--group-merge). + +Also, if we have duplicate detection for logical expression, and we do not start physical +optimization until after full plan enumeration, then we do not actually need to do duplicate +detection of physical expressions, since they are derivative of the deduplicated logical +expressions. + +### Fingerprint Matching Algorithm + +When an expression is added to the memo table, it will first calculate the fingerprint of the +expression. The memo table will compare this fingerprint with every fingerprint in the memo table to +check if we have seen this expression before (in any group). While this is effectively a scan +through every expression, supporting the fingerprint table with an B+tree index will speed up this +operation dramatically (since these fingerprints can be sorted by expression / relation kind). + +If there are no identical fingerprints, then there is no duplicate expression, and we can safely +add the expression into the memo table. However, if there are matching fingerprints, we need to +further check for false positives due to hash collisions. + +We do full exact match equality checks with every expression that had a fingerprint match. If there +are no exact matches, then we can safely add the expression into the memo table. However, if we find +an exact match (note that there can be at most one exact match since we have an invariant that there +cannot be duplicate expressions), then we know that the expression we are trying to add already +exists in the memo table. + +### Fingerprinting + Group Merge + +There is a slight problem with the algorithm described above. It does not account for when a child +group has merged into another group. + +For example, let's say we have groups 1, 2, and 3. We insert an expression Join(1, 2) into the +memo table with its fingerprint calculated with groups 1 and 2. It is possible that we find out that +groups 2 and 3 need to merged. This means that Join(1, 2) and Join (1, 3) are actually identical +expressions, and the fingerprinting strategies for expressions described above do not handle this. + +We will solve this problem by adding allowing multiple fingerprints to reference the same logical +expression, and we will generate a new fingerprint for every expression that is affected by a group +merge / every expression who's parent group now has a new root group. + +In the above scenario, we will find every expression in the memo table that has group 2 as a child. +For each expression, we will generate another fingerprint with group 2 "rewritten" as group 3 in the +hash. Note that we _do not_ modify the original expression, we are simply adding another fingerprint +into the memo table. + +Finally, we need to handle when multiple groups in a group set are merged into another group set. +For example, if a left group set { 1, 2, 3, 4, 5 } with root 1 needs to be merged into a right group +set { 6, 7, 8, 9, 10 } with root 6, then we need to generate a new fingerprint for every expression +in groups 1, 2, 3, 4, and 5 with group 1 "rewritten" as group 6. + +More formally, we are maintaining this invariant: +**For every expression, there exists a fingerprint that maps back to the expression that uses the** +**root groups of their children to calculate the hash.** + +For example, if we have a group set { 1, 3, 5 } with root group 1 and group set { 2, 4, 6 } with +root group 2, the fingerprint of Join(5, 4) should really be a fingerprint of Join(1, 2). + +This invariant means that when we are checking if some expression already exists, we should use the +root groups of the child groups in our expression to calculate the fingerprint, and we can guarantee +that no fingerprint matches implies no duplicates. + +A further implication of this invariant means that new fingerprints need to be generated every time +we merge groups. If we have a left group set { 1, 3, 5 } with root group 1 and right group set +{ 2, 4, 6 } with root group 2, and we merge the first group set into the second, then every +expression that has a child group of 1, 3, or 5 now has a stale fingerprint that uses root group 1 +instead of root group 2. + +Thus, when we merge the left group into the right group, we need to do the following: + +1. Gather the group set, i.e. every single group that has root group 1 (iterate) +2. Retrieve every single expression that has a child group in the group set (via junction table) +3. Generate a new fingerprint for each expression and add it into the memo table + +The speed of steps 2 and 3 above are largely dependent on the backing DBMS. However, we can support +step 1 directly in the union find data structure by maintain a circular linked list for every set. +Each group now tracks both a `parent` pointer and a `next` pointer. When merging / unioning a set +into another set, we swap the `next` pointers of the two roots to maintain the circular linked list. +This allows us to do step 1 in linear time relative to the size of the group set. + +### Discovered Duplicates + +The above algorithm has one more problem: merging groups can cause the memo table to "discover" that +there are duplicate expressions in the memo table. + +Here is an example: let's say we have the following groups, each with one expression (note that the +example will work even with multiple expressions): + +1. `Scan(1)` +2. `Scan(2)` +3. `Filter(1)` +4. `Filter(2)` +5. `Filter(4)` +6. `Join(3, 4)` +7. `Join(3, 5)` +8. `Sort(6)` +9. `Sort(7)` + +Note how groups 5 is just a second filter on top of group 2. Suppose that we find out that +`(Filter(4) = Filter(Filter(2))) == Filter(2)`. In that case, we need to merge groups 4 and 5. The +problem here is that groups 6 and 7 are considered separate groups, but we have now discovered that +they are actually the same. The same is true for groups 8 and 9. In this scenario, the merging of +groups has "generated" a duplicate expression. + +However, this is not as big of a problem as it might seem. The issue we want to avoid is lots of +duplicate work or even an infinite loop of rule application. Observe that if we apply a rule to both +the expression in group 6 and group 7 that we will get the same exact expression. + +For example, if we apply join commutativity to the expression in group 6 (`Join(3, 4)`), we would +add `Join(4, 3)` into group 6. When we apply join commutativity to the expression in group 7 +(`Join(3, 5)`), we would get back `Join(5, 3)`. However, the memo table will detect this as a +duplicate because it will use the root group of 4 and 5 to generate the fingerprint and see that +`Join(4, 3)` already exists. Again, similar logic applies for groups 8 and 9. + +At a high level, almost all of our operations are lazy. Work does not need to be done unless it is +absolutely necessary for correctness. By allowing some amount of duplicates, we get some nice +properties with respect to parallelizing memo table access. + +## Efficiency and Parallelism + +Fingerprinting by itself is very efficient, as creating a fingerprint and looking up a fingerprint +can be made quite efficient with indexes. The real concern here is that merging two groups is very, +very expensive. Depending on the workload, it is both possible that the amortized cost is low or +that group merging takes a majority of the work. + +However, we must remember that we want to parallelize access to the memo table. The above algorithms +are notably **read and append only**. There is never a point where we need to update an expression +to maintain invariants. This is important, as it means that we can add and lookup expression and +groups _without having to take any locks_. If we enforce a serializable isolation level, then every +method on the memo table can be done in parallel with relatively low contention due to there being +zero write-write conflicts. diff --git a/optd-mvp/entities.md b/optd-mvp/entities.md index cfd082a..fc13a39 100644 --- a/optd-mvp/entities.md +++ b/optd-mvp/entities.md @@ -8,7 +8,7 @@ This assumes that you already have the `sqlite3` binary installed. First, make s $ cargo install sea-orm-cli ``` -Make sure your working directory is in the crate root: +Make sure your working directory is in the crate root (not workspace): ```sh $ cd optd-mvp diff --git a/optd-mvp/src/expression/logical_expression.rs b/optd-mvp/src/expression/logical_expression.rs index c7918de..4ddf46e 100644 --- a/optd-mvp/src/expression/logical_expression.rs +++ b/optd-mvp/src/expression/logical_expression.rs @@ -2,24 +2,20 @@ //! //! FIXME: All fields are placeholders. //! -//! TODO Remove dead code. //! TODO Figure out if each relation should be in a different submodule. //! TODO This entire file is a WIP. -#![allow(dead_code)] - use crate::{entities::*, memo::GroupId}; use fxhash::hash; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug)] pub enum LogicalExpression { Scan(Scan), Filter(Filter), Join(Join), } -/// FIXME: Figure out how to make everything unsigned instead of signed. impl LogicalExpression { pub fn kind(&self) -> i16 { match self { @@ -29,11 +25,6 @@ impl LogicalExpression { } } - /// Definitions of custom fingerprinting strategies for each kind of logical expression. - pub fn fingerprint(&self) -> i64 { - self.fingerprint_with_rewrite(&[]) - } - /// Calculates the fingerprint of a given expression, but replaces all of the children group IDs /// with a new group ID if it is listed in the input `rewrites` list. /// @@ -55,13 +46,14 @@ impl LogicalExpression { let kind = self.kind() as u16 as usize; let hash = match self { - LogicalExpression::Scan(scan) => hash(scan.table_schema.as_str()), + LogicalExpression::Scan(scan) => hash(scan.table.as_str()), LogicalExpression::Filter(filter) => { hash(&rewrite(filter.child).0) ^ hash(filter.expression.as_str()) } LogicalExpression::Join(join) => { - hash(&rewrite(join.left).0) - ^ hash(&rewrite(join.right).0) + // Make sure that there is a difference between `Join(A, B)` and `Join(B, A)`. + hash(&(rewrite(join.left).0 + 1)) + ^ hash(&(rewrite(join.right).0 + 2)) ^ hash(join.expression.as_str()) } }; @@ -69,27 +61,69 @@ impl LogicalExpression { // Mask out the bottom 16 bits of `hash` and replace them with `kind`. ((hash & !0xFFFF) | kind) as i64 } + + /// Checks equality between two expressions, with both expression rewriting their child group + /// IDs according to the input `rewrites` list. + pub fn eq_with_rewrite(&self, other: &Self, rewrites: &[(GroupId, GroupId)]) -> bool { + // Closure that rewrites a group ID if needed. + let rewrite = |x: GroupId| { + if rewrites.is_empty() { + return x; + } + + if let Some(i) = rewrites.iter().position(|(curr, _new)| &x == curr) { + assert_eq!(rewrites[i].0, x); + rewrites[i].1 + } else { + x + } + }; + + match (self, other) { + (LogicalExpression::Scan(scan_left), LogicalExpression::Scan(scan_right)) => { + scan_left.table == scan_right.table + } + (LogicalExpression::Filter(filter_left), LogicalExpression::Filter(filter_right)) => { + rewrite(filter_left.child) == rewrite(filter_right.child) + && filter_left.expression == filter_right.expression + } + (LogicalExpression::Join(join_left), LogicalExpression::Join(join_right)) => { + rewrite(join_left.left) == rewrite(join_right.left) + && rewrite(join_left.right) == rewrite(join_right.right) + && join_left.expression == join_right.expression + } + _ => false, + } + } + + pub fn children(&self) -> Vec { + match self { + LogicalExpression::Scan(_) => vec![], + LogicalExpression::Filter(filter) => vec![filter.child], + LogicalExpression::Join(join) => vec![join.left, join.right], + } + } } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct Scan { - table_schema: String, + table: String, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct Filter { child: GroupId, expression: String, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct Join { left: GroupId, right: GroupId, expression: String, } -/// TODO Use a macro instead. +/// TODO Use a macro. impl From for LogicalExpression { fn from(value: logical_expression::Model) -> Self { match value.kind { @@ -110,7 +144,7 @@ impl From for LogicalExpression { } } -/// TODO Use a macro instead. +/// TODO Use a macro. impl From for logical_expression::Model { fn from(value: LogicalExpression) -> logical_expression::Model { fn create_logical_expression( @@ -152,7 +186,9 @@ mod build { use crate::expression::LogicalExpression; pub fn scan(table_schema: String) -> LogicalExpression { - LogicalExpression::Scan(Scan { table_schema }) + LogicalExpression::Scan(Scan { + table: table_schema, + }) } pub fn filter(child_group: GroupId, expression: String) -> LogicalExpression { diff --git a/optd-mvp/src/expression/physical_expression.rs b/optd-mvp/src/expression/physical_expression.rs index aaaa9e7..9c451b7 100644 --- a/optd-mvp/src/expression/physical_expression.rs +++ b/optd-mvp/src/expression/physical_expression.rs @@ -2,12 +2,9 @@ //! //! FIXME: All fields are placeholders. //! -//! TODO Remove dead code. //! TODO Figure out if each operator should be in a different submodule. //! TODO This entire file is a WIP. -#![allow(dead_code)] - use crate::{entities::*, memo::GroupId}; use serde::{Deserialize, Serialize}; @@ -36,7 +33,7 @@ pub struct HashJoin { expression: String, } -/// TODO Use a macro instead. +/// TODO Use a macro. impl From for PhysicalExpression { fn from(value: physical_expression::Model) -> Self { match value.kind { @@ -57,7 +54,7 @@ impl From for PhysicalExpression { } } -/// TODO Use a macro instead. +/// TODO Use a macro. impl From for physical_expression::Model { fn from(value: PhysicalExpression) -> physical_expression::Model { fn create_physical_expression( @@ -100,23 +97,4 @@ mod build { pub fn table_scan(table_schema: String) -> PhysicalExpression { PhysicalExpression::TableScan(TableScan { table_schema }) } - - pub fn filter(child_group: GroupId, expression: String) -> PhysicalExpression { - PhysicalExpression::Filter(PhysicalFilter { - child: child_group, - expression, - }) - } - - pub fn hash_join( - left_group: GroupId, - right_group: GroupId, - expression: String, - ) -> PhysicalExpression { - PhysicalExpression::HashJoin(HashJoin { - left: left_group, - right: right_group, - expression, - }) - } } diff --git a/optd-mvp/src/lib.rs b/optd-mvp/src/lib.rs index 506eee4..48a4c78 100644 --- a/optd-mvp/src/lib.rs +++ b/optd-mvp/src/lib.rs @@ -18,8 +18,6 @@ pub const DATABASE_FILENAME: &str = "sqlite.db"; pub const DATABASE_URL: &str = "sqlite:./sqlite.db?mode=rwc"; /// An error type wrapping all the different kinds of error the optimizer might raise. -/// -/// TODO more docs. #[derive(Error, Debug)] pub enum OptimizerError { #[error("SeaORM error")] diff --git a/optd-mvp/src/memo/persistent/implementation.rs b/optd-mvp/src/memo/persistent/implementation.rs index d7e7c25..70b10e1 100644 --- a/optd-mvp/src/memo/persistent/implementation.rs +++ b/optd-mvp/src/memo/persistent/implementation.rs @@ -18,6 +18,7 @@ use sea_orm::{ entity::{IntoActiveModel, NotSet, Set}, Database, }; +use std::collections::HashSet; impl PersistentMemo { /// Creates a new `PersistentMemo` struct by connecting to a database defined at @@ -90,12 +91,55 @@ impl PersistentMemo { // For every group along the path that we walked, set their parent id pointer to the root. // This allows for an amortized O(1) cost for `get_root_group`. for group in path { - self.update_group_parent(GroupId(group.id), root_id).await?; + let mut active_group = group.into_active_model(); + + // Update the group to point to the new parent. + active_group.parent_id = Set(Some(root_id.0)); + active_group.update(&self.db).await?; } Ok(root_id) } + /// Retrieves every group ID of groups that share the same root group with the input group. + /// + /// If a group does not exist in the cycle, returns a [`MemoError::UnknownGroup`] error. + /// + /// The group records form a union-find data structure that also maintains a circular linked + /// list in every set that allows us to iterate over all elements in a set in linear time. + pub async fn get_group_set(&self, group_id: GroupId) -> OptimizerResult> { + // Iterate over the circular linked list until we reach ourselves again. + let base_group = self.get_group(group_id).await?; + + // The only case when `next_id` is set to `None` is if the current group is a root, which + // means that this group is the only group in the set. + if base_group.next_id.is_none() { + assert!(base_group.parent_id.is_none()); + return Ok(vec![group_id]); + } + + // Iterate over the circular linked list until we see ourselves again, collecting nodes + // along the way. + let mut set = vec![group_id]; + let mut next_id = base_group + .next_id + .expect("next pointer cannot be null if it is in a cycle"); + loop { + let curr_group = self.get_group(GroupId(next_id)).await?; + + if curr_group.id == group_id.0 { + break; + } + + set.push(GroupId(curr_group.id)); + next_id = curr_group + .next_id + .expect("next pointer cannot be null if it is in a cycle"); + } + + Ok(set) + } + /// Retrieves a [`physical_expression::Model`] given a [`PhysicalExpressionId`]. /// /// If the physical expression does not exist, returns a @@ -227,30 +271,6 @@ impl PersistentMemo { Ok(old_id) } - /// Updates / replaces a group's parent group. Optionally returns the previous parent. - /// - /// If either of the groups do not exist, returns a [`MemoError::UnknownGroup`] error. - pub async fn update_group_parent( - &self, - group_id: GroupId, - parent_id: GroupId, - ) -> OptimizerResult> { - // First retrieve the group record. - let mut group = self.get_group(group_id).await?.into_active_model(); - - // Check that the parent group exists. - let _ = self.get_group(parent_id).await?; - - // Update the group to point to the new parent. - let old_parent = group.parent_id; - group.parent_id = Set(Some(parent_id.0)); - group.update(&self.db).await?; - - // Note that the `unwrap` here is unwrapping the `ActiveValue`, not the `Option`. - let old_parent = old_parent.unwrap().map(GroupId); - Ok(old_parent) - } - /// Adds a logical expression to an existing group via its ID. /// /// The caller is required to pass in a slice of [`GroupId`] that represent the child groups of @@ -265,8 +285,6 @@ impl PersistentMemo { /// /// If the memo table detects that the input is unique, it will insert the expression into the /// input group and return an `Ok(Ok(expression_id))`. - /// - /// FIXME Check that all of the children are reduced groups? pub async fn add_logical_expression_to_group( &self, group_id: GroupId, @@ -323,7 +341,7 @@ impl PersistentMemo { kind: Set(kind), hash: Set(hash), }; - let _ = fingerprint::Entity::insert(fingerprint) + fingerprint::Entity::insert(fingerprint) .exec(&self.db) .await?; @@ -379,8 +397,6 @@ impl PersistentMemo { /// This function assumes that the child groups of the expression are currently roots of their /// group sets. For example, if G1 and G2 should be merged, and G1 is the root, then the input /// expression should _not_ have G2 as a child, and should be replaced with G1. - /// - /// TODO Check that all of the children are root groups? How to do this? pub async fn is_duplicate_logical_expression( &self, logical_expression: &LogicalExpression, @@ -422,8 +438,16 @@ impl PersistentMemo { let expr_id = LogicalExpressionId(potential_match.logical_expression_id); let (group_id, expr) = self.get_logical_expression(expr_id).await?; - // Check for an exact match. - if &expr == logical_expression { + // We need to add the root groups of the new expression to the rewrites vector. + // TODO make this much more efficient by making rewrites a hash map, potentially im::HashMap. + let mut rewrites = rewrites.clone(); + for child_id in expr.children() { + let root_id = self.get_root_group(child_id).await?; + rewrites.push((child_id, root_id)); + } + + // Check for an exact match after rewrites. + if logical_expression.eq_with_rewrite(&expr, &rewrites) { match_id = Some((group_id, expr_id)); // There should be at most one duplicate expression, so we can break here. @@ -447,8 +471,6 @@ impl PersistentMemo { /// /// If the expression does not exist, this function will create a new group and a new /// expression, returning brand new IDs for both. - /// - /// FIXME Check that all of the children are reduced groups? pub async fn add_group( &self, logical_expression: LogicalExpression, @@ -513,10 +535,101 @@ impl PersistentMemo { kind: Set(kind), hash: Set(hash), }; - let _ = fingerprint::Entity::insert(fingerprint) + fingerprint::Entity::insert(fingerprint) .exec(&self.db) .await?; Ok(Ok((GroupId(group_id), LogicalExpressionId(expr_id)))) } + + /// Merges two groups sets together. + /// + /// If either of the input groups do not exist, returns a [`MemoError::UnknownGroup`] error. + /// + /// TODO write docs. + /// TODO highly inefficient, need to understand metrics and performance testing. + /// TODO Optimization: add rank / size into data structure + pub async fn merge_groups( + &self, + left_group_id: GroupId, + right_group_id: GroupId, + ) -> OptimizerResult { + // Without a rank / size field, we have no way of determining which set is better to merge + // into the other. So we will arbitrarily choose to merge the left group into the right + // group here. If rank is added in the future, then merge the smaller set into the larger. + + let left_root_id = self.get_root_group(left_group_id).await?; + let left_root = self.get_group(left_root_id).await?; + // A `None` next pointer means it should technically be pointing to itself. + let left_next = left_root.next_id.unwrap_or(left_root_id.0); + let mut active_left_root = left_root.into_active_model(); + + let right_root_id = self.get_root_group(right_group_id).await?; + let right_root = self.get_group(right_root_id).await?; + // A `None` next pointer means it should technically be pointing to itself. + let right_next = right_root.next_id.unwrap_or(right_root_id.0); + let mut active_right_root = right_root.into_active_model(); + + // Before we actually update the group records, We first need to generate new fingerprints + // for every single expression that has a child group in the left set. + // TODO make this more efficient, this code is doing double work from `get_group_set`. + let group_set_ids = self.get_group_set(left_group_id).await?; + let mut left_group_models = Vec::with_capacity(group_set_ids.len()); + for &group_id in &group_set_ids { + left_group_models.push(self.get_group(group_id).await?); + } + + // Retrieve every single expression that has a child group in the left set. + let left_group_expressions: Vec> = left_group_models + .load_many_to_many( + logical_expression::Entity, + logical_children::Entity, + &self.db, + ) + .await?; + + // Need to replace every single occurrence of groups in the set with the new root. + let rewrites: Vec<(GroupId, GroupId)> = group_set_ids + .iter() + .map(|&group_id| (group_id, right_root_id)) + .collect(); + + // For each expression, generate a new fingerprint. + let mut seen = HashSet::new(); + for model in left_group_expressions.into_iter().flatten() { + let expr_id = model.id; + + // There may be duplicates in the expressions list. + if seen.contains(&expr_id) { + continue; + } else { + seen.insert(expr_id); + } + + let logical_expression: LogicalExpression = model.into(); + let hash = logical_expression.fingerprint_with_rewrite(&rewrites); + + let fingerprint = fingerprint::ActiveModel { + id: NotSet, + logical_expression_id: Set(expr_id), + kind: Set(logical_expression.kind()), + hash: Set(hash), + }; + fingerprint::Entity::insert(fingerprint) + .exec(&self.db) + .await?; + } + + // Update the left group root to point to the right group root. + active_left_root.parent_id = Set(Some(right_root_id.0)); + + // Swap the next pointers of each root to maintain the circular linked list. + active_left_root.next_id = Set(Some(right_next)); + active_right_root.next_id = Set(Some(left_next)); + + active_left_root.update(&self.db).await?; + active_right_root.update(&self.db).await?; + + Ok(right_root_id) + } } diff --git a/optd-mvp/src/memo/persistent/tests.rs b/optd-mvp/src/memo/persistent/tests.rs index 3dcddd6..be3115c 100644 --- a/optd-mvp/src/memo/persistent/tests.rs +++ b/optd-mvp/src/memo/persistent/tests.rs @@ -114,8 +114,6 @@ async fn test_simple_tree() { ); // Create two join expression that should be in the same group. - // TODO: Eventually, the predicates will be in their own table, and the predicate representation - // will be a foreign key. For now, we represent them as strings. let join1 = join(scan_id_1, scan_id_2, "t1.a = t2.b".to_string()); let join2 = join(scan_id_2, scan_id_1, "t1.a = t2.b".to_string()); @@ -143,7 +141,7 @@ async fn test_simple_tree() { memo.cleanup().await; } -/// Tests basic group merging. See comments in the test itself for more information. +/// Tests a single group merge. See comments in the test itself for more information. #[ignore] #[tokio::test] async fn test_simple_group_link() { @@ -191,22 +189,283 @@ async fn test_simple_group_link() { // The above tells the application that the expression already exists in the memo, specifically // under `existing_group`. Thus, we should link these two groups together. - // Here, we arbitrarily choose to link group 1 into group 2. - memo.update_group_parent(join_group_1, join_group_2) - .await - .unwrap(); + memo.merge_groups(join_group_1, join_group_2).await.unwrap(); let test_root_1 = memo.get_root_group(join_group_1).await.unwrap(); let test_root_2 = memo.get_root_group(join_group_2).await.unwrap(); assert_eq!(test_root_1, test_root_2); - // TODO(Connor) - // - // We now need to find all logical expressions that had group 1 (or whatever the root group of - // the set that group 1 belongs to is, in this case it is just group 1) as a child, and add a - // new fingerprint for each one that uses group 2 as a child instead. - // - // In order to do this, we need to iterate through every group in group 1's set. + memo.cleanup().await; +} + +#[ignore] +#[tokio::test] +async fn test_group_merge_ladder() { + let memo = PersistentMemo::new().await; + memo.cleanup().await; + + // Build up a tree of true filters that should be collapsed into a single table scan. + let scan_base = scan("t1".to_string()); + let (scan_id, _) = memo.add_group(scan_base, &[]).await.unwrap().ok().unwrap(); + + let filter0 = filter(scan_id, "true".to_string()); + let (filter_id_0, _) = memo + .add_group(filter0, &[scan_id]) + .await + .unwrap() + .ok() + .unwrap(); + + let filter1 = filter(filter_id_0, "true".to_string()); + let (filter_id_1, _) = memo + .add_group(filter1, &[scan_id]) + .await + .unwrap() + .ok() + .unwrap(); + + let filter2 = filter(filter_id_1, "true".to_string()); + let (filter_id_2, _) = memo + .add_group(filter2, &[scan_id]) + .await + .unwrap() + .ok() + .unwrap(); + + let filter3 = filter(filter_id_2, "true".to_string()); + let (filter_id_3, _) = memo + .add_group(filter3, &[scan_id]) + .await + .unwrap() + .ok() + .unwrap(); + + let mut groups = vec![scan_id, filter_id_0, filter_id_1, filter_id_2, filter_id_3]; + + let m0 = memo.merge_groups(filter_id_3, filter_id_2).await.unwrap(); + let m1 = memo.merge_groups(filter_id_2, filter_id_1).await.unwrap(); + let m2 = memo.merge_groups(filter_id_1, filter_id_0).await.unwrap(); + let root = memo.merge_groups(filter_id_0, scan_id).await.unwrap(); + groups.extend_from_slice(&[m0, m1, m2, root]); + + for group_id in groups { + assert_eq!(root, memo.get_root_group(group_id).await.unwrap()); + } + + memo.cleanup().await; +} + +/// Tests merging a bunch of groups together in order to prevent duplicates from being added. +#[ignore] +#[tokio::test] +async fn test_group_merge() { + let memo = PersistentMemo::new().await; + memo.cleanup().await; + + // Create a base group. + let scan1 = scan("t1".to_string()); + let (scan_id_1, _) = memo.add_group(scan1, &[]).await.unwrap().ok().unwrap(); + + // Create a bunch of equivalent groups. + let filter0 = filter(scan_id_1, "true".to_string()); + let filter1 = filter(scan_id_1, "1 < 2".to_string()); + let filter2 = filter(scan_id_1, "2 > 1".to_string()); + let filter3 = filter(scan_id_1, "42 != 100".to_string()); + let filter4 = filter(scan_id_1, "10000 > 0".to_string()); + let filter5 = filter(scan_id_1, "1 + 2 = 3".to_string()); + let filter6 = filter(scan_id_1, "true OR false".to_string()); + let filter7 = filter(scan_id_1, "(1 + 1 > -1 AND true) OR false".to_string()); + let (filter_id_0, _) = memo + .add_group(filter0, &[scan_id_1]) + .await + .unwrap() + .ok() + .unwrap(); + let (filter_id_1, _) = memo + .add_group(filter1, &[scan_id_1]) + .await + .unwrap() + .ok() + .unwrap(); + let (filter_id_2, _) = memo + .add_group(filter2, &[scan_id_1]) + .await + .unwrap() + .ok() + .unwrap(); + let (filter_id_3, _) = memo + .add_group(filter3, &[scan_id_1]) + .await + .unwrap() + .ok() + .unwrap(); + let (filter_id_4, _) = memo + .add_group(filter4, &[scan_id_1]) + .await + .unwrap() + .ok() + .unwrap(); + let (filter_id_5, _) = memo + .add_group(filter5, &[scan_id_1]) + .await + .unwrap() + .ok() + .unwrap(); + let (filter_id_6, _) = memo + .add_group(filter6, &[scan_id_1]) + .await + .unwrap() + .ok() + .unwrap(); + let (filter_id_7, _) = memo + .add_group(filter7, &[scan_id_1]) + .await + .unwrap() + .ok() + .unwrap(); + let filters = vec![ + filter_id_0, + filter_id_1, + filter_id_2, + filter_id_3, + filter_id_4, + filter_id_5, + filter_id_6, + filter_id_7, + ]; + + // Merge them all together. + let quarter_0 = memo.merge_groups(filters[0], filters[1]).await.unwrap(); + let quarter_1 = memo.merge_groups(filters[2], filters[3]).await.unwrap(); + let quarter_2 = memo.merge_groups(filters[4], filters[5]).await.unwrap(); + let quarter_3 = memo.merge_groups(filters[6], filters[7]).await.unwrap(); + let semi_0 = memo.merge_groups(quarter_0, quarter_1).await.unwrap(); + let semi_1 = memo.merge_groups(quarter_2, quarter_3).await.unwrap(); + let final_id = memo.merge_groups(semi_0, semi_1).await.unwrap(); + + // Check that the group set is properly representative. + { + let set = memo.get_group_set(final_id).await.unwrap(); + assert_eq!(set.len(), 8); + for id in set { + assert!(filters.contains(&id)); + } + } + + // Create another base group. + let scan2 = scan("t2".to_string()); + let (scan_id_2, _) = memo.add_group(scan2, &[]).await.unwrap().ok().unwrap(); + + // Add a join group. + let join0 = join(filter_id_0, scan_id_2, "t1.a = t2.a".to_string()); + let (join_group_id, join_expr_id) = memo + .add_group(join0, &[filter_id_0, scan_id_2]) + .await + .unwrap() + .ok() + .unwrap(); + + // Adding the duplicate join expressions should return a duplication error containing the IDs of + // the already existing group and expression. + for filter_id in filters { + let join_test = join(filter_id, scan_id_2, "t1.a = t2.a".to_string()); + let (join_group_id_test, join_expr_id_test) = memo + .add_group(join_test, &[filter_id, scan_id_2]) + .await + .unwrap() + .err() + .unwrap(); + assert_eq!(join_group_id, join_group_id_test); + assert_eq!(join_expr_id, join_expr_id_test); + } + + memo.cleanup().await; +} + +/// Tests the exact same scenario as in the "Discovered Duplicates" section in `DESIGN.md`. +#[ignore] +#[tokio::test] +async fn test_cascading_merge() { + let memo = PersistentMemo::new().await; + memo.cleanup().await; + + // Create the base groups. + let scan1 = scan("t1".to_string()); + let (g1, _) = memo.add_group(scan1, &[]).await.unwrap().ok().unwrap(); + let scan2 = scan("t2".to_string()); + let (g2, _) = memo.add_group(scan2, &[]).await.unwrap().ok().unwrap(); + + let filter1 = filter(g1, "x > 1000".to_string()); + let (g3, _) = memo.add_group(filter1, &[g1]).await.unwrap().ok().unwrap(); + + // Create two groups that will need to be merged. + let filter2a = filter(g2, "a < 42".to_string()); + let (g4, _) = memo.add_group(filter2a, &[g2]).await.unwrap().ok().unwrap(); + let filter2b = filter(g4, "a < 42 AND 1 = 1".to_string()); + let (g5, _) = memo.add_group(filter2b, &[g4]).await.unwrap().ok().unwrap(); + + // Create groups that are dependent on the to-be-merged groups. + let join1 = join(g3, g4, "t1.x = t2.a".to_string()); + let (g6, _) = memo + .add_group(join1, &[g3, g4]) + .await + .unwrap() + .ok() + .unwrap(); + let join2 = join(g3, g5, "t1.x = t2.a".to_string()); + let (g7, _) = memo + .add_group(join2, &[g3, g5]) + .await + .unwrap() + .ok() + .unwrap(); + + // Create more groups that are dependent on the to-be-merged groups. + // TODO actually use a sort expression instead of a `filter` placeholder. + let sort1 = filter(g6, "ORDER BY a".to_string()); + let (g8, _) = memo.add_group(sort1, &[g6]).await.unwrap().ok().unwrap(); + + let sort2 = filter(g7, "ORDER BY a".to_string()); + let (g9, _) = memo.add_group(sort2, &[g7]).await.unwrap().ok().unwrap(); + + // Now that everything is set up, we can merge groups 4 and 5 to begin the cascading process. + let filter_root = memo.merge_groups(g4, g5).await.unwrap(); + assert_eq!(memo.get_root_group(g4).await.unwrap(), filter_root); + assert_eq!(memo.get_root_group(g5).await.unwrap(), filter_root); + + // After merging, the join groups (6 and 7) are technically identical, but we have not merged + // them together yet. However, applying rules will reveal that they are identical, and we will + // know that they need to get merged. + let join1_commute = join(g4, g3, "t1.x = t2.a".to_string()); + let join1_commute_id = memo + .add_logical_expression_to_group(g6, join1_commute, &[g4, g3]) + .await + .unwrap() + .ok() + .unwrap(); + + // Adding this expression should now result in a duplication error and return the above ID. + let join2_commute = join(g5, g3, "t1.x = t2.a".to_string()); + let (existing_g6, existing_id) = memo + .add_logical_expression_to_group(g7, join2_commute, &[g5, g3]) + .await + .unwrap() + .err() + .unwrap(); + assert_eq!(existing_g6, g6); + assert_eq!(existing_id, join1_commute_id); + + // Since the memo table has told us these are duplicates, we can now merge groups 6 and 7. + let join_root = memo.merge_groups(g6, g7).await.unwrap(); + assert_eq!(memo.get_root_group(g6).await.unwrap(), join_root); + assert_eq!(memo.get_root_group(g7).await.unwrap(), join_root); + + // Do a similar thing for the sort groups. We'll skip the expression adding for now and just + // merge them immediately, but remember that the application should observe a duplicate + // somewhere in the memo table before deciding to merge groups. + let sort_root = memo.merge_groups(g8, g9).await.unwrap(); + assert_eq!(memo.get_root_group(g8).await.unwrap(), sort_root); + assert_eq!(memo.get_root_group(g9).await.unwrap(), sort_root); memo.cleanup().await; } diff --git a/optd-mvp/src/migrator/memo/m20241127_000001_group.rs b/optd-mvp/src/migrator/memo/m20241127_000001_group.rs index d5bbe0e..59b5a09 100644 --- a/optd-mvp/src/migrator/memo/m20241127_000001_group.rs +++ b/optd-mvp/src/migrator/memo/m20241127_000001_group.rs @@ -28,8 +28,8 @@ //! `cost` foreign key reference to a cost record (FIXME). See the //! [section](#best-physical-plan-winner) below for more details. //! -//! Finally, we maintain a union-find graph structure embedded in the group records. -//! TODO write more information about this once this is implemented. +//! Finally, we maintain a union-find graph structure embedded in the group records. See the +//! `DESIGN.md` document for more information. //! //! # Entity Relationships //! From d30eb2fbc91151ec759ea6fc9666467a8d93a01f Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Fri, 6 Dec 2024 17:04:10 -0500 Subject: [PATCH 10/10] use root group newtype --- optd-mvp/src/memo/mod.rs | 18 +++++- .../src/memo/persistent/implementation.rs | 63 ++++++++++--------- 2 files changed, 47 insertions(+), 34 deletions(-) diff --git a/optd-mvp/src/memo/mod.rs b/optd-mvp/src/memo/mod.rs index 08b74db..5147fe1 100644 --- a/optd-mvp/src/memo/mod.rs +++ b/optd-mvp/src/memo/mod.rs @@ -8,15 +8,27 @@ use thiserror::Error; /// A new type of an integer identifying a unique group. #[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)] #[serde(transparent)] -pub struct GroupId(pub i32); +pub struct GroupId(pub(crate) i32); + +/// A new type of an integer identifying a root group / canonical group ID of a group set. +#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)] +#[serde(transparent)] +pub struct RootGroupId(pub(crate) i32); + +/// A [`RootGroupId`] is always [`GroupId`], but not vice versa. +impl From for GroupId { + fn from(value: RootGroupId) -> Self { + Self(value.0) + } +} /// A new type of an integer identifying a unique logical expression. #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct LogicalExpressionId(pub i32); +pub struct LogicalExpressionId(i32); /// A new type of an integer identifying a unique physical expression. #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct PhysicalExpressionId(pub i32); +pub struct PhysicalExpressionId(i32); /// A status enum representing the different states a group can be during query optimization. #[repr(u8)] diff --git a/optd-mvp/src/memo/persistent/implementation.rs b/optd-mvp/src/memo/persistent/implementation.rs index 70b10e1..293bd50 100644 --- a/optd-mvp/src/memo/persistent/implementation.rs +++ b/optd-mvp/src/memo/persistent/implementation.rs @@ -10,7 +10,9 @@ use super::PersistentMemo; use crate::{ entities::*, expression::{LogicalExpression, PhysicalExpression}, - memo::{GroupId, GroupStatus, LogicalExpressionId, MemoError, PhysicalExpressionId}, + memo::{ + GroupId, GroupStatus, LogicalExpressionId, MemoError, PhysicalExpressionId, RootGroupId, + }, OptimizerResult, DATABASE_URL, }; use sea_orm::{ @@ -74,31 +76,27 @@ impl PersistentMemo { /// /// This function uses the path compression optimization, which amortizes the cost to a single /// lookup (theoretically in constant time, but we must be wary of the I/O roundtrip). - pub async fn get_root_group(&self, group_id: GroupId) -> OptimizerResult { - let mut curr_group = self.get_group(group_id).await?; - - // Traverse up the path and find the root group, keeping track of groups we have visited. - let mut path = vec![]; - while let Some(parent_id) = curr_group.parent_id { - let next_group = self.get_group(GroupId(parent_id)).await?; - path.push(curr_group); - curr_group = next_group; - } + pub async fn get_root_group(&self, group_id: GroupId) -> OptimizerResult { + let curr_group = self.get_group(group_id).await?; + + // If we have no parent, then we are at the root. + let Some(parent_id) = curr_group.parent_id else { + return Ok(RootGroupId(curr_group.id)); + }; - let root_id = GroupId(curr_group.id); + // Recursively find the root group ID. + let root_id = Box::pin(self.get_root_group(GroupId(parent_id))).await?; // Path Compression Optimization: // For every group along the path that we walked, set their parent id pointer to the root. // This allows for an amortized O(1) cost for `get_root_group`. - for group in path { - let mut active_group = group.into_active_model(); + let mut active_group = curr_group.into_active_model(); - // Update the group to point to the new parent. - active_group.parent_id = Set(Some(root_id.0)); - active_group.update(&self.db).await?; - } + // Update the group to point to the new parent. + active_group.parent_id = Set(Some(root_id.0)); + active_group.update(&self.db).await?; - Ok(root_id) + Ok(RootGroupId(root_id.0)) } /// Retrieves every group ID of groups that share the same root group with the input group. @@ -246,8 +244,11 @@ impl PersistentMemo { Ok(old_status) } - /// Updates / replaces a group's best physical plan (winner). Optionally returns the previous - /// winner's physical expression ID. + /// Updates / replaces a group set's best physical plan (winner). Optionally returns the + /// previous winner's physical expression ID. + /// + /// Note that we want to update the root group and not a child group that has been merged into + /// a different group. /// /// If the group does not exist, returns a [`MemoError::UnknownGroup`] error. /// @@ -255,11 +256,11 @@ impl PersistentMemo { /// updated from another thread by comparing against the cost of the plan. pub async fn update_group_winner( &self, - group_id: GroupId, + root_group_id: RootGroupId, physical_expression_id: PhysicalExpressionId, ) -> OptimizerResult> { // First retrieve the group record. - let mut group = self.get_group(group_id).await?.into_active_model(); + let mut group = self.get_group(root_group_id.into()).await?.into_active_model(); // Update the group to point to the new winner. let old_id = group.winner; @@ -331,7 +332,7 @@ impl PersistentMemo { let mut rewrites = vec![]; for &child_id in children { let root_id = self.get_root_group(child_id).await?; - rewrites.push((child_id, root_id)); + rewrites.push((child_id, root_id.into())); } let hash = new_expr.fingerprint_with_rewrite(&rewrites); @@ -413,7 +414,7 @@ impl PersistentMemo { let mut rewrites = vec![]; for &child_id in children { let root_id = self.get_root_group(child_id).await?; - rewrites.push((child_id, root_id)); + rewrites.push((child_id, root_id.into())); } let fingerprint = logical_expression.fingerprint_with_rewrite(&rewrites); @@ -443,7 +444,7 @@ impl PersistentMemo { let mut rewrites = rewrites.clone(); for child_id in expr.children() { let root_id = self.get_root_group(child_id).await?; - rewrites.push((child_id, root_id)); + rewrites.push((child_id, root_id.into())); } // Check for an exact match after rewrites. @@ -525,7 +526,7 @@ impl PersistentMemo { let mut rewrites = vec![]; for &child_id in children { let root_id = self.get_root_group(child_id).await?; - rewrites.push((child_id, root_id)); + rewrites.push((child_id, root_id.into())); } let hash = new_logical_expression.fingerprint_with_rewrite(&rewrites); @@ -553,19 +554,19 @@ impl PersistentMemo { &self, left_group_id: GroupId, right_group_id: GroupId, - ) -> OptimizerResult { + ) -> OptimizerResult { // Without a rank / size field, we have no way of determining which set is better to merge // into the other. So we will arbitrarily choose to merge the left group into the right // group here. If rank is added in the future, then merge the smaller set into the larger. let left_root_id = self.get_root_group(left_group_id).await?; - let left_root = self.get_group(left_root_id).await?; + let left_root = self.get_group(left_root_id.into()).await?; // A `None` next pointer means it should technically be pointing to itself. let left_next = left_root.next_id.unwrap_or(left_root_id.0); let mut active_left_root = left_root.into_active_model(); let right_root_id = self.get_root_group(right_group_id).await?; - let right_root = self.get_group(right_root_id).await?; + let right_root = self.get_group(right_root_id.into()).await?; // A `None` next pointer means it should technically be pointing to itself. let right_next = right_root.next_id.unwrap_or(right_root_id.0); let mut active_right_root = right_root.into_active_model(); @@ -591,7 +592,7 @@ impl PersistentMemo { // Need to replace every single occurrence of groups in the set with the new root. let rewrites: Vec<(GroupId, GroupId)> = group_set_ids .iter() - .map(|&group_id| (group_id, right_root_id)) + .map(|&group_id| (group_id, right_root_id.into())) .collect(); // For each expression, generate a new fingerprint.