├── .github ├── mergify.yml └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── rust-toolchain ├── rustfmt.toml ├── src ├── binder │ ├── expression │ │ ├── agg_func.rs │ │ ├── binary_op.rs │ │ └── mod.rs │ ├── mod.rs │ ├── statement │ │ └── mod.rs │ └── table │ │ ├── join.rs │ │ ├── mod.rs │ │ └── subquery.rs ├── catalog │ └── mod.rs ├── catalog_v2 │ ├── catalog.rs │ ├── catalog_set.rs │ ├── constants.rs │ ├── entry │ │ ├── mod.rs │ │ ├── scalar_function_catalog_entry.rs │ │ ├── schema_catalog_entry.rs │ │ ├── table_catalog_entry.rs │ │ └── table_function_catalog_entry.rs │ ├── errors.rs │ └── mod.rs ├── cli.rs ├── common │ ├── cast.rs │ ├── create_info.rs │ └── mod.rs ├── db.rs ├── execution │ ├── column_binding_resolver.rs │ ├── expression_executor.rs │ ├── mod.rs │ ├── physical_plan │ │ ├── mod.rs │ │ ├── physical_column_data_scan.rs │ │ ├── physical_create_table.rs │ │ ├── physical_dummy_scan.rs │ │ ├── physical_explain.rs │ │ ├── physical_expression_scan.rs │ │ ├── physical_filter.rs │ │ ├── physical_insert.rs │ │ ├── physical_limit.rs │ │ ├── physical_projection.rs │ │ └── physical_table_scan.rs │ ├── physical_plan_generator.rs │ ├── util.rs │ └── volcano_executor │ │ ├── column_data_scan.rs │ │ ├── create_table.rs │ │ ├── dummy_scan.rs │ │ ├── expression_scan.rs │ │ ├── filter.rs │ │ ├── insert.rs │ │ ├── limit.rs │ │ ├── mod.rs │ │ ├── projection.rs │ │ └── table_scan.rs ├── executor │ ├── aggregate │ │ ├── count.rs │ │ ├── hash_agg.rs │ │ ├── hash_utils.rs │ │ ├── min_max.rs │ │ ├── mod.rs │ │ ├── simple_agg.rs │ │ └── sum.rs │ ├── array_compute.rs │ ├── evaluator.rs │ ├── filter.rs │ ├── join │ │ ├── cross_join.rs │ │ ├── hash_join.rs │ │ └── mod.rs │ ├── limit.rs │ ├── mod.rs │ ├── order.rs │ ├── project.rs │ └── table_scan.rs ├── function │ ├── cast │ │ ├── cast_function.rs │ │ ├── cast_rules.rs │ │ ├── default_cast.rs │ │ └── mod.rs │ ├── comparison │ │ ├── comparison_function.rs │ │ ├── default_comparison.rs │ │ └── mod.rs │ ├── conjunction │ │ ├── conjunction_function.rs │ │ ├── default_conjunction.rs │ │ └── mod.rs │ ├── errors.rs │ ├── mod.rs │ ├── scalar │ │ ├── arithmetic_function.rs │ │ ├── mod.rs │ │ └── scalar_function.rs │ └── table │ │ ├── mod.rs │ │ ├── read_csv.rs │ │ ├── seq_table_scan.rs │ │ ├── sqlrs_columns.rs │ │ ├── sqlrs_tables.rs │ │ └── table_function.rs ├── lib.rs ├── main.rs ├── main_entry │ ├── client_context.rs │ ├── db.rs │ ├── errors.rs │ ├── mod.rs │ ├── pending_query_result.rs │ ├── prepared_statement_data.rs │ ├── query_context.rs │ └── query_result.rs ├── optimizer │ ├── core │ │ ├── mod.rs │ │ ├── opt_expr.rs │ │ ├── pattern.rs │ │ └── rule.rs │ ├── expr_rewriter.rs │ ├── expr_visitor.rs │ ├── heuristic │ │ ├── batch.rs │ │ ├── graph.rs │ │ ├── matcher.rs │ │ ├── mod.rs │ │ └── optimizer.rs │ ├── input_ref_rewriter.rs │ ├── mod.rs │ ├── physical_rewriter.rs │ ├── plan_node │ │ ├── dummy.rs │ │ ├── logical_agg.rs │ │ ├── logical_filter.rs │ │ ├── logical_join.rs │ │ ├── logical_limit.rs │ │ ├── logical_order.rs │ │ ├── logical_project.rs │ │ ├── logical_table_scan.rs │ │ ├── mod.rs │ │ ├── physical_cross_join.rs │ │ ├── physical_filter.rs │ │ ├── physical_hash_agg.rs │ │ ├── physical_hash_join.rs │ │ ├── physical_limit.rs │ │ ├── physical_order.rs │ │ ├── physical_project.rs │ │ ├── physical_simple_agg.rs │ │ ├── physical_table_scan.rs │ │ └── plan_node_traits.rs │ ├── plan_rewriter.rs │ ├── plan_visitor.rs │ └── rules │ │ ├── column_pruning.rs │ │ ├── combine_operators.rs │ │ ├── mod.rs │ │ ├── physical_rewrite.rs │ │ ├── pushdown_limit.rs │ │ ├── pushdown_predicates.rs │ │ ├── simplification.rs │ │ └── util.rs ├── parser │ └── mod.rs ├── planner │ ├── mod.rs │ ├── select.rs │ └── util.rs ├── planner_v2 │ ├── binder │ │ ├── bind_context.rs │ │ ├── binding.rs │ │ ├── errors.rs │ │ ├── expression │ │ │ ├── bind_cast_expression.rs │ │ │ ├── bind_column_ref_expression.rs │ │ │ ├── bind_comparison_expression.rs │ │ │ ├── bind_conjunction_expression.rs │ │ │ ├── bind_constant_expression.rs │ │ │ ├── bind_function_expression.rs │ │ │ ├── bind_reference_expression.rs │ │ │ ├── column_binding.rs │ │ │ └── mod.rs │ │ ├── expression_binder │ │ │ ├── column_alias_binder.rs │ │ │ └── mod.rs │ │ ├── mod.rs │ │ ├── query_node │ │ │ ├── bind_result_modifier.rs │ │ │ ├── bind_select_node.rs │ │ │ ├── mod.rs │ │ │ ├── plan_result_modifier.rs │ │ │ └── plan_select_node.rs │ │ ├── sqlparser_util.rs │ │ ├── statement │ │ │ ├── bind_copy.rs │ │ │ ├── bind_create.rs │ │ │ ├── bind_explain.rs │ │ │ ├── bind_explain_table.rs │ │ │ ├── bind_insert.rs │ │ │ ├── bind_select.rs │ │ │ ├── bind_show_tables.rs │ │ │ └── mod.rs │ │ └── tableref │ │ │ ├── bind_base_table_ref.rs │ │ │ ├── bind_dummy_table_ref.rs │ │ │ ├── bind_expression_list_ref.rs │ │ │ ├── bind_table_function.rs │ │ │ ├── mod.rs │ │ │ ├── plan_base_table_ref.rs │ │ │ ├── plan_dummy_table_ref.rs │ │ │ ├── plan_expression_list_ref.rs │ │ │ └── plan_table_function.rs │ ├── constants.rs │ ├── errors.rs │ ├── expression_binder.rs │ ├── expression_iterator.rs │ ├── function_binder.rs │ ├── logical_operator_visitor.rs │ ├── mod.rs │ └── operator │ │ ├── logical_create_table.rs │ │ ├── logical_dummy_scan.rs │ │ ├── logical_explain.rs │ │ ├── logical_expression_get.rs │ │ ├── logical_filter.rs │ │ ├── logical_get.rs │ │ ├── logical_insert.rs │ │ ├── logical_limit.rs │ │ ├── logical_projection.rs │ │ └── mod.rs ├── storage │ ├── csv.rs │ ├── memory.rs │ └── mod.rs ├── storage_v2 │ ├── local_storage.rs │ └── mod.rs ├── types │ └── mod.rs ├── types_v2 │ ├── errors.rs │ ├── mod.rs │ ├── types.rs │ └── values.rs └── util │ ├── mod.rs │ └── tree_render.rs └── tests ├── csv ├── department.csv ├── employee.csv ├── state.csv ├── t1.csv └── t2.csv ├── planner ├── column-pruning.planner.sql ├── column-pruning.yml ├── combine-operators.planner.sql ├── combine-operators.yml ├── limit-pushdown.planner.sql ├── limit-pushdown.yml ├── predicate-pushdown.planner.sql └── predicate-pushdown.yml ├── slt ├── aggregation.slt ├── alias.slt ├── comparison_function.slt ├── conjunction_function.slt ├── create_table.slt ├── csv │ ├── csv.slt │ ├── state1.csv │ └── state2.csv ├── distinct.slt ├── explain.slt ├── filter.slt ├── insert_table.slt ├── join.slt ├── join_filter.slt ├── limit.slt ├── order.slt ├── pragma.slt ├── scalar_function.slt ├── select.slt ├── subquery.slt ├── table_function.slt └── time.slt ├── sqllogictest ├── Cargo.toml ├── README.md ├── src │ └── lib.rs └── tests │ └── sqllogictest.rs └── sqlplannertest ├── Cargo.toml ├── README.md ├── src ├── bin │ └── apply.rs └── lib.rs └── tests └── plannertest.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | fmt: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: actions-rs/toolchain@v1 17 | with: 18 | profile: minimal 19 | components: rustfmt, clippy 20 | - uses: actions/cache@v3 21 | with: 22 | path: | 23 | ~/.cargo/registry/index/ 24 | ~/.cargo/registry/cache/ 25 | ~/.cargo/git/db/ 26 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 27 | - name: Check code format 28 | run: cargo fmt --all -- --check 29 | - name: Clippy 30 | run: cargo clippy --all-targets --all-features --locked -- -D warnings 31 | 32 | test: 33 | runs-on: ubuntu-latest 34 | steps: 35 | - uses: actions/checkout@v3 36 | - uses: actions-rs/toolchain@v1 37 | with: 38 | profile: minimal 39 | - uses: actions/cache@v3 40 | with: 41 | path: | 42 | ~/.cargo/registry/index/ 43 | ~/.cargo/registry/cache/ 44 | ~/.cargo/git/db/ 45 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 46 | - uses: taiki-e/install-action@nextest 47 | - name: Test 48 | run: cargo nextest run --no-fail-fast --all-features --locked 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.vscode 3 | /tpch-dbgen 4 | /tpch-data 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sqlrs" 3 | version = "0.4.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1" 10 | arrow = { version = "28", features = ["prettyprint", "simd"] } 11 | tokio = { version = "1", features = ["full"] } 12 | futures-async-stream = "0.2" 13 | futures = "0.3" 14 | thiserror = "1" 15 | sqlparser = { version = "0.28", features = ["serde"] } 16 | itertools = "0.10" 17 | downcast-rs = "1" 18 | paste = "1" 19 | rustyline = "10" 20 | dirs = "4" 21 | ahash = { version = "0.8", default-features = false, features = ["runtime-rng"] } 22 | petgraph = "0.6" 23 | enum_dispatch = "0.3" 24 | lazy_static = "1" 25 | strum = "0.24" 26 | strum_macros = "0.24" 27 | ordered-float = "3.0" 28 | derive-new = "0.5.9" 29 | log = "0.4" 30 | env_logger = "0.10" 31 | derive_builder = "0.12.0" 32 | async-stream = "0.3" 33 | 34 | [dev-dependencies] 35 | test-case = "2" 36 | pretty_assertions = "1.3.0" 37 | 38 | [workspace] 39 | members = [ 40 | "tests/sqllogictest", 41 | "tests/sqlplannertest", 42 | ] 43 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | default: check 2 | 3 | fmt_check: 4 | cargo fmt --all -- --check 5 | 6 | fmt: 7 | cargo fmt --all 8 | 9 | clippy_check: 10 | cargo clippy --workspace --all-targets --all-features --locked -- -D warnings 11 | 12 | clippy: 13 | cargo clippy --workspace --all-targets --all-features --fix --allow-dirty --allow-staged 14 | 15 | build: 16 | cargo build --all-targets --all-features 17 | 18 | planner_test_build: 19 | cargo run -p sqlplannertest-test --bin apply 20 | 21 | planner_test: 22 | cargo nextest run -p sqlplannertest-test 23 | 24 | test: 25 | cargo nextest run --workspace --no-fail-fast --all-features --locked 26 | 27 | check: fmt_check clippy_check build test 28 | 29 | clean: 30 | cargo clean 31 | 32 | run: 33 | cargo run --release 34 | 35 | run_v2: 36 | ENABLE_V2=1 cargo run --release 37 | 38 | debug: 39 | RUST_BACKTRACE=1 cargo run 40 | 41 | debug_v2: 42 | ENABLE_V2=1 RUST_BACKTRACE=1 cargo run 43 | 44 | debug_v2_log: 45 | RUST_LOG='sqlrs::planner=debug,sqlrs::execution=debug' ENABLE_V2=1 RUST_BACKTRACE=1 cargo run 46 | 47 | TPCH_DBGEN_PATH := tpch-dbgen 48 | TPCH_DBGEN_DATA := tpch-data 49 | TPCH_SCALE := 1 50 | 51 | $(TPCH_DBGEN_PATH): 52 | mkdir -p target 53 | git clone https://github.com/electrum/tpch-dbgen.git $(TPCH_DBGEN_PATH) 54 | 55 | tpch: $(TPCH_DBGEN_PATH) 56 | make -C $(TPCH_DBGEN_PATH) 57 | cd $(TPCH_DBGEN_PATH) && ./dbgen -f -s $(TPCH_SCALE) && mkdir -p tbl && mv *.tbl tbl 58 | make tpch_gen_query 59 | make tpch_collect_data 60 | 61 | tpch_gen_query: 62 | cd $(TPCH_DBGEN_PATH) && \ 63 | mkdir -p ./gen-queries && \ 64 | export DSS_QUERY=./queries && \ 65 | for ((i=1;i<=22;i++)); do ./qgen -v -d -c -s $${i} $${i} > ./gen-queries/tpch-q$${i}.sql; done 66 | 67 | tpch_collect_data: 68 | mkdir -p $(TPCH_DBGEN_DATA) 69 | mv $(TPCH_DBGEN_PATH)/tbl $(TPCH_DBGEN_DATA) 70 | mv $(TPCH_DBGEN_PATH)/gen-queries $(TPCH_DBGEN_DATA) 71 | cp -r $(TPCH_DBGEN_PATH)/answers $(TPCH_DBGEN_DATA) 72 | 73 | tpch_clean: 74 | rm -rf $(TPCH_DBGEN_PATH) 75 | rm -rf $(TPCH_DBGEN_DATA) 76 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2022-07-29 2 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | comment_width = 150 2 | format_code_in_doc_comments = true 3 | format_macro_bodies = true 4 | format_macro_matchers = true 5 | normalize_comments = true 6 | normalize_doc_attributes = true 7 | imports_granularity = "Module" 8 | group_imports = "StdExternalCrate" 9 | reorder_imports = true 10 | wrap_comments = true 11 | -------------------------------------------------------------------------------- /src/catalog_v2/catalog_set.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use super::{CatalogEntry, CatalogError}; 4 | 5 | /// The Catalog Set stores (key, value) map of a set of CatalogEntries 6 | #[derive(Clone, Debug, Default)] 7 | pub struct CatalogSet { 8 | /// The set of catalog entries, entry index to entry 9 | entries: HashMap, 10 | /// Mapping of string to catalog entry index 11 | mapping: HashMap, 12 | /// The current catalog entry index 13 | current_entry: usize, 14 | } 15 | 16 | impl CatalogSet { 17 | pub fn create_entry(&mut self, name: String, entry: CatalogEntry) -> Result<(), CatalogError> { 18 | if self.mapping.get(&name).is_some() { 19 | return Err(CatalogError::CatalogEntryExists(name)); 20 | } 21 | self.current_entry += 1; 22 | self.entries.insert(self.current_entry, entry); 23 | self.mapping.insert(name, self.current_entry); 24 | Ok(()) 25 | } 26 | 27 | pub fn get_entry(&self, name: String) -> Result { 28 | if let Some(index) = self.mapping.get(&name) { 29 | if let Some(entry) = self.entries.get(index) { 30 | return Ok(entry.clone()); 31 | } 32 | } 33 | Err(CatalogError::CatalogEntryNotExists(name)) 34 | } 35 | 36 | pub fn get_mut_entry(&mut self, name: String) -> Result<&mut CatalogEntry, CatalogError> { 37 | if let Some(index) = self.mapping.get(&name) { 38 | if let Some(entry) = self.entries.get_mut(index) { 39 | return Ok(entry); 40 | } 41 | } 42 | Err(CatalogError::CatalogEntryNotExists(name)) 43 | } 44 | 45 | pub fn replace_entry( 46 | &mut self, 47 | name: String, 48 | new_entry: CatalogEntry, 49 | ) -> Result<(), CatalogError> { 50 | if let Some(old_entry_index) = self.mapping.get(&name) { 51 | if self.entries.get(old_entry_index).is_some() { 52 | self.entries.insert(*old_entry_index, new_entry); 53 | return Ok(()); 54 | } 55 | } 56 | Err(CatalogError::CatalogEntryNotExists(name)) 57 | } 58 | 59 | pub fn scan_entries(&self, callback: &F) -> Vec 60 | where 61 | F: Fn(&CatalogEntry) -> bool, 62 | { 63 | let mut result = vec![]; 64 | for (_, entry) in self.entries.iter() { 65 | if callback(entry) { 66 | result.push(entry.clone()); 67 | } 68 | } 69 | result 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/catalog_v2/constants.rs: -------------------------------------------------------------------------------- 1 | pub static DEFAULT_SCHEMA: &str = "main"; 2 | -------------------------------------------------------------------------------- /src/catalog_v2/entry/mod.rs: -------------------------------------------------------------------------------- 1 | mod scalar_function_catalog_entry; 2 | mod schema_catalog_entry; 3 | mod table_catalog_entry; 4 | mod table_function_catalog_entry; 5 | 6 | use derive_new::new; 7 | pub use scalar_function_catalog_entry::*; 8 | pub use schema_catalog_entry::*; 9 | pub use table_catalog_entry::*; 10 | pub use table_function_catalog_entry::*; 11 | 12 | #[derive(Clone, Debug)] 13 | pub enum CatalogEntry { 14 | SchemaCatalogEntry(SchemaCatalogEntry), 15 | TableCatalogEntry(TableCatalogEntry), 16 | TableFunctionCatalogEntry(TableFunctionCatalogEntry), 17 | ScalarFunctionCatalogEntry(ScalarFunctionCatalogEntry), 18 | } 19 | 20 | impl CatalogEntry { 21 | pub fn default_schema_catalog_entry(oid: usize, schema: String) -> Self { 22 | Self::SchemaCatalogEntry(SchemaCatalogEntry::new(oid, schema)) 23 | } 24 | } 25 | 26 | #[allow(dead_code)] 27 | #[derive(new, Clone, Debug)] 28 | pub struct CatalogEntryBase { 29 | /// The object identifier of the entry 30 | pub(crate) oid: usize, 31 | /// The name of the entry 32 | pub(crate) name: String, 33 | } 34 | -------------------------------------------------------------------------------- /src/catalog_v2/entry/scalar_function_catalog_entry.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::CatalogEntryBase; 4 | use crate::function::ScalarFunction; 5 | 6 | #[derive(new, Clone, Debug)] 7 | pub struct ScalarFunctionCatalogEntry { 8 | #[allow(dead_code)] 9 | pub(crate) base: CatalogEntryBase, 10 | #[allow(dead_code)] 11 | pub(crate) functions: Vec, 12 | } 13 | -------------------------------------------------------------------------------- /src/catalog_v2/entry/table_catalog_entry.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use derive_new::new; 4 | 5 | use super::CatalogEntryBase; 6 | use crate::types_v2::LogicalType; 7 | 8 | #[allow(dead_code)] 9 | #[derive(Clone, Debug)] 10 | pub struct TableCatalogEntry { 11 | pub(crate) base: CatalogEntryBase, 12 | pub(crate) schema_base: CatalogEntryBase, 13 | pub(crate) storage: DataTable, 14 | /// A list of columns that are part of this table 15 | pub(crate) columns: Vec, 16 | /// A map of column name to column index 17 | pub(crate) name_map: HashMap, 18 | } 19 | 20 | impl TableCatalogEntry { 21 | pub fn new( 22 | oid: usize, 23 | table: String, 24 | schema_base: CatalogEntryBase, 25 | storage: DataTable, 26 | ) -> Self { 27 | let mut name_map = HashMap::new(); 28 | let mut columns = vec![]; 29 | storage 30 | .column_definitions 31 | .iter() 32 | .enumerate() 33 | .for_each(|(idx, col)| { 34 | columns.push(col.clone()); 35 | name_map.insert(col.name.clone(), idx); 36 | }); 37 | Self { 38 | base: CatalogEntryBase::new(oid, table), 39 | schema_base, 40 | storage, 41 | columns, 42 | name_map, 43 | } 44 | } 45 | } 46 | 47 | /// DataTable represents a physical table on disk 48 | #[derive(new, Clone, Debug, PartialEq, Eq, Hash)] 49 | pub struct DataTable { 50 | /// The table info 51 | pub(crate) info: DataTableInfo, 52 | /// The set of physical columns stored by this DataTable 53 | pub(crate) column_definitions: Vec, 54 | } 55 | 56 | #[derive(new, Clone, Debug, PartialEq, Eq, Hash)] 57 | pub struct DataTableInfo { 58 | /// schema of the table 59 | pub(crate) schema: String, 60 | /// name of the table 61 | pub(crate) table: String, 62 | } 63 | 64 | /// A column of a table 65 | #[derive(new, Clone, Debug, PartialEq, Eq, Hash)] 66 | pub struct ColumnDefinition { 67 | /// The name of the entry 68 | pub(crate) name: String, 69 | /// The type of the column 70 | pub(crate) ty: LogicalType, 71 | } 72 | -------------------------------------------------------------------------------- /src/catalog_v2/entry/table_function_catalog_entry.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::CatalogEntryBase; 4 | use crate::function::TableFunction; 5 | 6 | #[derive(new, Clone, Debug)] 7 | pub struct TableFunctionCatalogEntry { 8 | #[allow(dead_code)] 9 | pub(crate) base: CatalogEntryBase, 10 | #[allow(dead_code)] 11 | pub(crate) functions: Vec, 12 | } 13 | -------------------------------------------------------------------------------- /src/catalog_v2/errors.rs: -------------------------------------------------------------------------------- 1 | #[derive(thiserror::Error, Debug)] 2 | pub enum CatalogError { 3 | #[error("CatalogEntry: {0} already exists")] 4 | CatalogEntryExists(String), 5 | #[error("CatalogEntry: {0} not exists")] 6 | CatalogEntryNotExists(String), 7 | #[error("CatalogEntry type not match")] 8 | CatalogEntryTypeNotMatch, 9 | #[error("Catalog locked, please retry")] 10 | CatalogLockedError, 11 | } 12 | -------------------------------------------------------------------------------- /src/catalog_v2/mod.rs: -------------------------------------------------------------------------------- 1 | mod catalog; 2 | mod catalog_set; 3 | mod constants; 4 | mod entry; 5 | mod errors; 6 | 7 | pub use catalog::*; 8 | pub use catalog_set::*; 9 | pub use constants::*; 10 | pub use entry::*; 11 | pub use errors::*; 12 | -------------------------------------------------------------------------------- /src/common/cast.rs: -------------------------------------------------------------------------------- 1 | use arrow::array::{Array, BooleanArray, Date32Array}; 2 | 3 | use crate::function::FunctionError; 4 | 5 | /// Downcast an Arrow Array to a concrete type 6 | macro_rules! downcast_value { 7 | ($Value:expr, $Type:ident) => {{ 8 | use std::any::type_name; 9 | $Value.as_any().downcast_ref::<$Type>().ok_or_else(|| { 10 | FunctionError::CastError(format!("could not cast value to {}", type_name::<$Type>())) 11 | })? 12 | }}; 13 | } 14 | 15 | /// Downcast ArrayRef to BooleanArray 16 | pub fn as_boolean_array(array: &dyn Array) -> Result<&BooleanArray, FunctionError> { 17 | Ok(downcast_value!(array, BooleanArray)) 18 | } 19 | 20 | // Downcast ArrayRef to Date32Array 21 | pub fn as_date32_array(array: &dyn Array) -> Result<&Date32Array, FunctionError> { 22 | Ok(downcast_value!(array, Date32Array)) 23 | } 24 | -------------------------------------------------------------------------------- /src/common/create_info.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use crate::catalog_v2::ColumnDefinition; 4 | use crate::function::{ScalarFunction, TableFunction}; 5 | 6 | #[derive(new, Debug, Clone)] 7 | pub struct CreateInfoBase { 8 | pub(crate) schema: String, 9 | } 10 | 11 | #[derive(new, Debug, Clone)] 12 | pub struct CreateTableInfo { 13 | pub(crate) base: CreateInfoBase, 14 | /// Table name to insert to 15 | pub(crate) table: String, 16 | /// List of columns of the table 17 | pub(crate) columns: Vec, 18 | } 19 | 20 | #[derive(new)] 21 | pub struct CreateTableFunctionInfo { 22 | pub(crate) base: CreateInfoBase, 23 | /// Function name 24 | pub(crate) name: String, 25 | /// Functions with different arguments 26 | pub(crate) functions: Vec, 27 | } 28 | 29 | #[derive(new)] 30 | pub struct CreateScalarFunctionInfo { 31 | pub(crate) base: CreateInfoBase, 32 | /// Function name 33 | pub(crate) name: String, 34 | /// Functions with different arguments 35 | pub(crate) functions: Vec, 36 | } 37 | -------------------------------------------------------------------------------- /src/common/mod.rs: -------------------------------------------------------------------------------- 1 | mod cast; 2 | mod create_info; 3 | 4 | pub use cast::*; 5 | pub use create_info::*; 6 | -------------------------------------------------------------------------------- /src/execution/column_binding_resolver.rs: -------------------------------------------------------------------------------- 1 | use crate::planner_v2::{ 2 | BoundColumnRefExpression, BoundExpression, BoundExpressionBase, BoundReferenceExpression, 3 | ColumnBinding, LogicalOperator, LogicalOperatorVisitor, 4 | }; 5 | 6 | #[derive(Default)] 7 | pub struct ColumnBindingResolver { 8 | bindings: Vec, 9 | } 10 | 11 | impl LogicalOperatorVisitor for ColumnBindingResolver { 12 | fn visit_operator(&mut self, op: &mut LogicalOperator) { 13 | { 14 | self.visit_operator_children(op); 15 | self.visit_operator_expressions(op); 16 | self.bindings = op.get_column_bindings(); 17 | } 18 | } 19 | 20 | fn visit_replace_column_ref(&self, expr: &BoundColumnRefExpression) -> Option { 21 | assert!(expr.depth == 0); 22 | // check the current set of column bindings to see which index corresponds to the column 23 | // reference 24 | if let Some(idx) = self.bindings.iter().position(|e| expr.binding == *e) { 25 | let expr = BoundReferenceExpression::new( 26 | BoundExpressionBase::new(expr.base.alias.clone(), expr.base.return_type.clone()), 27 | idx, 28 | ); 29 | return Some(BoundExpression::BoundReferenceExpression(expr)); 30 | } 31 | 32 | // could not bind the column reference, this should never happen and indicates a bug in the 33 | // code generate an error message 34 | panic!( 35 | "Failed to bind column reference {} [{}.{}] (bindings: {:?}), ", 36 | expr.base.alias, expr.binding.table_idx, expr.binding.column_idx, self.bindings 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/execution/mod.rs: -------------------------------------------------------------------------------- 1 | mod column_binding_resolver; 2 | mod expression_executor; 3 | mod physical_plan; 4 | mod physical_plan_generator; 5 | mod volcano_executor; 6 | use std::sync::Arc; 7 | mod util; 8 | 9 | use arrow::error::ArrowError; 10 | pub use column_binding_resolver::*; 11 | use derive_new::new; 12 | pub use expression_executor::*; 13 | pub use physical_plan::*; 14 | pub use physical_plan_generator::*; 15 | pub use util::*; 16 | pub use volcano_executor::*; 17 | 18 | use crate::catalog_v2::CatalogError; 19 | use crate::function::FunctionError; 20 | use crate::main_entry::ClientContext; 21 | use crate::types_v2::TypeError; 22 | 23 | static LOGGING_TARGET: &str = "sqlrs::execution"; 24 | 25 | #[derive(new)] 26 | pub struct ExecutionContext { 27 | pub(crate) client_context: Arc, 28 | } 29 | 30 | impl ExecutionContext { 31 | pub fn clone_client_context(&self) -> Arc { 32 | self.client_context.clone() 33 | } 34 | } 35 | 36 | #[derive(thiserror::Error, Debug)] 37 | pub enum ExecutorError { 38 | #[error("catalog error: {0}")] 39 | CatalogError( 40 | #[source] 41 | #[from] 42 | CatalogError, 43 | ), 44 | #[error("arrow error: {0}")] 45 | ArrowError( 46 | #[source] 47 | #[from] 48 | ArrowError, 49 | ), 50 | #[error("type error: {0}")] 51 | TypeError( 52 | #[source] 53 | #[from] 54 | TypeError, 55 | ), 56 | #[error("function error: {0}")] 57 | FunctionError( 58 | #[source] 59 | #[from] 60 | FunctionError, 61 | ), 62 | #[error("Executor internal error: {0}")] 63 | InternalError(String), 64 | } 65 | -------------------------------------------------------------------------------- /src/execution/physical_plan/mod.rs: -------------------------------------------------------------------------------- 1 | mod physical_column_data_scan; 2 | mod physical_create_table; 3 | mod physical_dummy_scan; 4 | mod physical_explain; 5 | mod physical_expression_scan; 6 | mod physical_filter; 7 | mod physical_insert; 8 | mod physical_limit; 9 | mod physical_projection; 10 | mod physical_table_scan; 11 | 12 | use derive_new::new; 13 | pub use physical_column_data_scan::*; 14 | pub use physical_create_table::*; 15 | pub use physical_dummy_scan::*; 16 | pub use physical_explain::*; 17 | pub use physical_expression_scan::*; 18 | pub use physical_filter::*; 19 | pub use physical_insert::*; 20 | pub use physical_limit::*; 21 | pub use physical_projection::*; 22 | pub use physical_table_scan::*; 23 | 24 | use crate::planner_v2::BoundExpression; 25 | 26 | #[derive(new, Default, Clone)] 27 | pub struct PhysicalOperatorBase { 28 | pub(crate) children: Vec, 29 | // The set of expressions contained within the operator, if any 30 | pub(crate) expressioins: Vec, 31 | } 32 | 33 | #[derive(Clone)] 34 | pub enum PhysicalOperator { 35 | PhysicalCreateTable(PhysicalCreateTable), 36 | PhysicalDummyScan(PhysicalDummyScan), 37 | PhysicalExpressionScan(PhysicalExpressionScan), 38 | PhysicalInsert(Box), 39 | PhysicalTableScan(PhysicalTableScan), 40 | PhysicalProjection(PhysicalProjection), 41 | PhysicalColumnDataScan(PhysicalColumnDataScan), 42 | PhysicalFilter(PhysicalFilter), 43 | PhysicalLimit(PhysicalLimit), 44 | } 45 | 46 | impl PhysicalOperator { 47 | pub fn children(&self) -> &[PhysicalOperator] { 48 | match self { 49 | PhysicalOperator::PhysicalCreateTable(op) => &op.base.children, 50 | PhysicalOperator::PhysicalExpressionScan(op) => &op.base.children, 51 | PhysicalOperator::PhysicalInsert(op) => &op.base.children, 52 | PhysicalOperator::PhysicalTableScan(op) => &op.base.children, 53 | PhysicalOperator::PhysicalProjection(op) => &op.base.children, 54 | PhysicalOperator::PhysicalDummyScan(op) => &op.base.children, 55 | PhysicalOperator::PhysicalColumnDataScan(op) => &op.base.children, 56 | PhysicalOperator::PhysicalFilter(op) => &op.base.children, 57 | PhysicalOperator::PhysicalLimit(op) => &op.base.children, 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/execution/physical_plan/physical_column_data_scan.rs: -------------------------------------------------------------------------------- 1 | use arrow::record_batch::RecordBatch; 2 | use derive_new::new; 3 | 4 | use super::PhysicalOperatorBase; 5 | 6 | /// The PhysicalColumnDataScan scans a Arrow RecordBatch 7 | #[derive(new, Clone)] 8 | pub struct PhysicalColumnDataScan { 9 | pub(crate) base: PhysicalOperatorBase, 10 | pub(crate) collection: Vec, 11 | } 12 | -------------------------------------------------------------------------------- /src/execution/physical_plan/physical_create_table.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::{PhysicalInsert, PhysicalOperator, PhysicalOperatorBase}; 4 | use crate::execution::PhysicalPlanGenerator; 5 | use crate::planner_v2::{BoundCreateTableInfo, LogicalCreateTable}; 6 | 7 | #[derive(new, Clone)] 8 | pub struct PhysicalCreateTable { 9 | #[new(default)] 10 | pub(crate) base: PhysicalOperatorBase, 11 | pub(crate) info: BoundCreateTableInfo, 12 | } 13 | 14 | impl PhysicalPlanGenerator { 15 | pub(crate) fn create_physical_create_table(&self, op: LogicalCreateTable) -> PhysicalOperator { 16 | if let Some(query) = op.info.query.clone() { 17 | // create table as select 18 | let query_plan = self.create_plan(*query); 19 | let base = PhysicalOperatorBase::new(vec![query_plan], vec![]); 20 | PhysicalOperator::PhysicalInsert(Box::new(PhysicalInsert::new_create_table_as( 21 | base, op.info, 22 | ))) 23 | } else { 24 | // create table 25 | PhysicalOperator::PhysicalCreateTable(PhysicalCreateTable::new(op.info)) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/execution/physical_plan/physical_dummy_scan.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::{PhysicalOperator, PhysicalOperatorBase}; 4 | use crate::execution::PhysicalPlanGenerator; 5 | use crate::planner_v2::LogicalDummyScan; 6 | 7 | #[derive(new, Clone)] 8 | pub struct PhysicalDummyScan { 9 | pub(crate) base: PhysicalOperatorBase, 10 | } 11 | 12 | impl PhysicalPlanGenerator { 13 | pub(crate) fn create_physical_dummy_scan(&self, op: LogicalDummyScan) -> PhysicalOperator { 14 | let base = self.create_physical_operator_base(op.base); 15 | PhysicalOperator::PhysicalDummyScan(PhysicalDummyScan::new(base)) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/execution/physical_plan/physical_explain.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use arrow::array::StringArray; 4 | use arrow::record_batch::RecordBatch; 5 | 6 | use super::{PhysicalColumnDataScan, PhysicalOperator}; 7 | use crate::execution::{PhysicalPlanGenerator, SchemaUtil}; 8 | use crate::planner_v2::LogicalExplain; 9 | use crate::util::tree_render::TreeRender; 10 | 11 | impl PhysicalPlanGenerator { 12 | pub(crate) fn create_physical_explain(&self, op: LogicalExplain) -> PhysicalOperator { 13 | let types = op.base.types.clone(); 14 | let logical_child = op.base.children[0].clone(); 15 | // optimized logical plan explain string 16 | let logical_plan_opt_string = TreeRender::logical_plan_tree(&logical_child); 17 | 18 | let physical_child = self.create_plan_internal(logical_child); 19 | // physical plan explain string 20 | let physical_plan_string = TreeRender::physical_plan_tree(&physical_child); 21 | 22 | let base = self.create_physical_operator_base(op.base); 23 | 24 | let schema = SchemaUtil::new_schema_ref(&["type".to_string(), "plan".to_string()], &types); 25 | let types_column = Arc::new(StringArray::from(vec![ 26 | "logical_plan".to_string(), 27 | "logical_plan_opt".to_string(), 28 | "physical_plan".to_string(), 29 | ])); 30 | let plans_column = Arc::new(StringArray::from(vec![ 31 | op.logical_plan, 32 | logical_plan_opt_string, 33 | physical_plan_string, 34 | ])); 35 | let collection = RecordBatch::try_new(schema, vec![types_column, plans_column]).unwrap(); 36 | PhysicalOperator::PhysicalColumnDataScan(PhysicalColumnDataScan::new( 37 | base, 38 | vec![collection], 39 | )) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/execution/physical_plan/physical_expression_scan.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::{PhysicalOperator, PhysicalOperatorBase}; 4 | use crate::execution::PhysicalPlanGenerator; 5 | use crate::planner_v2::{BoundExpression, LogicalExpressionGet}; 6 | use crate::types_v2::LogicalType; 7 | 8 | /// The PhysicalExpressionScan scans a set of expressions 9 | #[derive(new, Clone)] 10 | pub struct PhysicalExpressionScan { 11 | pub(crate) base: PhysicalOperatorBase, 12 | /// The types of the expressions 13 | pub(crate) expr_types: Vec, 14 | /// The set of expressions to scan 15 | pub(crate) expressions: Vec>, 16 | } 17 | 18 | impl PhysicalPlanGenerator { 19 | pub(crate) fn create_physical_expression_scan( 20 | &self, 21 | op: LogicalExpressionGet, 22 | ) -> PhysicalOperator { 23 | assert!(op.base.children.len() == 1); 24 | let base = self.create_physical_operator_base(op.base); 25 | PhysicalOperator::PhysicalExpressionScan(PhysicalExpressionScan::new( 26 | base, 27 | op.expr_types, 28 | op.expressions, 29 | )) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/execution/physical_plan/physical_filter.rs: -------------------------------------------------------------------------------- 1 | use super::{PhysicalOperator, PhysicalOperatorBase}; 2 | use crate::execution::PhysicalPlanGenerator; 3 | use crate::planner_v2::{BoundConjunctionExpression, LogicalFilter}; 4 | 5 | #[derive(Clone)] 6 | pub struct PhysicalFilter { 7 | pub(crate) base: PhysicalOperatorBase, 8 | } 9 | 10 | impl PhysicalFilter { 11 | pub fn new(mut base: PhysicalOperatorBase) -> Self { 12 | let expression = 13 | BoundConjunctionExpression::try_build_and_conjunction_expression(base.expressioins); 14 | base.expressioins = vec![expression]; 15 | Self { base } 16 | } 17 | } 18 | 19 | impl PhysicalPlanGenerator { 20 | pub(crate) fn create_physical_filter(&self, op: LogicalFilter) -> PhysicalOperator { 21 | assert!(op.base.children.len() == 1); 22 | let base = self.create_physical_operator_base(op.base); 23 | PhysicalOperator::PhysicalFilter(PhysicalFilter::new(base)) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/execution/physical_plan/physical_insert.rs: -------------------------------------------------------------------------------- 1 | use super::{PhysicalOperator, PhysicalOperatorBase}; 2 | use crate::catalog_v2::TableCatalogEntry; 3 | use crate::execution::PhysicalPlanGenerator; 4 | use crate::planner_v2::{BoundCreateTableInfo, LogicalInsert}; 5 | use crate::types_v2::LogicalType; 6 | 7 | #[derive(Clone)] 8 | pub struct PhysicalInsert { 9 | pub(crate) base: PhysicalOperatorBase, 10 | /// The insertion map ([table_index -> index in result, or INVALID_INDEX if not specified]) 11 | pub(crate) column_index_list: Vec, 12 | /// The expected types for the INSERT statement 13 | pub(crate) expected_types: Vec, 14 | /// The table to insert into, the table is none when create table as 15 | pub(crate) table: Option, 16 | /// For create table as statement 17 | pub(crate) create_table_info: Option, 18 | } 19 | 20 | impl PhysicalInsert { 21 | pub fn clone_with_base(&self, base: PhysicalOperatorBase) -> Self { 22 | Self { 23 | base, 24 | column_index_list: self.column_index_list.clone(), 25 | expected_types: self.expected_types.clone(), 26 | table: self.table.clone(), 27 | create_table_info: self.create_table_info.clone(), 28 | } 29 | } 30 | 31 | pub fn new_insert_into( 32 | base: PhysicalOperatorBase, 33 | column_index_list: Vec, 34 | expected_types: Vec, 35 | table: TableCatalogEntry, 36 | ) -> Self { 37 | Self { 38 | base, 39 | column_index_list, 40 | expected_types, 41 | table: Some(table), 42 | create_table_info: None, 43 | } 44 | } 45 | 46 | pub fn new_create_table_as( 47 | base: PhysicalOperatorBase, 48 | create_table_info: BoundCreateTableInfo, 49 | ) -> Self { 50 | Self { 51 | base, 52 | column_index_list: vec![], 53 | expected_types: vec![], 54 | table: None, 55 | create_table_info: Some(create_table_info), 56 | } 57 | } 58 | } 59 | 60 | impl PhysicalPlanGenerator { 61 | pub(crate) fn create_physical_insert(&self, op: LogicalInsert) -> PhysicalOperator { 62 | let base = self.create_physical_operator_base(op.base); 63 | PhysicalOperator::PhysicalInsert(Box::new(PhysicalInsert::new_insert_into( 64 | base, 65 | op.column_index_list, 66 | op.expected_types, 67 | op.table, 68 | ))) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/execution/physical_plan/physical_limit.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::{PhysicalOperator, PhysicalOperatorBase}; 4 | use crate::execution::PhysicalPlanGenerator; 5 | use crate::planner_v2::LogicalLimit; 6 | 7 | #[derive(new, Clone)] 8 | pub struct PhysicalLimit { 9 | pub(crate) base: PhysicalOperatorBase, 10 | pub(crate) limit: Option, 11 | pub(crate) offset: Option, 12 | } 13 | 14 | impl PhysicalPlanGenerator { 15 | pub(crate) fn create_physical_limit(&self, op: LogicalLimit) -> PhysicalOperator { 16 | let base = self.create_physical_operator_base(op.base); 17 | let limit = op.limit.map(|_| op.limit_value); 18 | let offset = op.offset.map(|_| op.offsert_value); 19 | PhysicalOperator::PhysicalLimit(PhysicalLimit::new(base, limit, offset)) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/execution/physical_plan/physical_projection.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::{PhysicalOperator, PhysicalOperatorBase}; 4 | use crate::execution::PhysicalPlanGenerator; 5 | use crate::planner_v2::LogicalProjection; 6 | 7 | #[derive(new, Clone)] 8 | pub struct PhysicalProjection { 9 | pub(crate) base: PhysicalOperatorBase, 10 | } 11 | 12 | impl PhysicalPlanGenerator { 13 | pub(crate) fn create_physical_projection(&self, op: LogicalProjection) -> PhysicalOperator { 14 | let base = self.create_physical_operator_base(op.base); 15 | PhysicalOperator::PhysicalProjection(PhysicalProjection::new(base)) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/execution/physical_plan/physical_table_scan.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::{PhysicalOperator, PhysicalOperatorBase}; 4 | use crate::execution::PhysicalPlanGenerator; 5 | use crate::function::{FunctionData, TableFunction}; 6 | use crate::planner_v2::LogicalGet; 7 | use crate::types_v2::LogicalType; 8 | 9 | #[derive(new, Clone)] 10 | pub struct PhysicalTableScan { 11 | pub(crate) base: PhysicalOperatorBase, 12 | pub(crate) function: TableFunction, 13 | pub(crate) bind_data: Option, 14 | /// The types of ALL columns that can be returned by the table function 15 | pub(crate) returned_types: Vec, 16 | /// The names of ALL columns that can be returned by the table function 17 | pub(crate) names: Vec, 18 | } 19 | 20 | impl PhysicalPlanGenerator { 21 | pub(crate) fn create_physical_table_scan(&self, op: LogicalGet) -> PhysicalOperator { 22 | let base = self.create_physical_operator_base(op.base); 23 | let plan = 24 | PhysicalTableScan::new(base, op.function, op.bind_data, op.returned_types, op.names); 25 | PhysicalOperator::PhysicalTableScan(plan) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/execution/physical_plan_generator.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use derive_new::new; 4 | use log::debug; 5 | 6 | use super::{ColumnBindingResolver, PhysicalOperator, PhysicalOperatorBase}; 7 | use crate::execution::LOGGING_TARGET; 8 | use crate::main_entry::ClientContext; 9 | use crate::planner_v2::{LogicalOperator, LogicalOperatorBase, LogicalOperatorVisitor}; 10 | use crate::util::tree_render::TreeRender; 11 | 12 | #[derive(new)] 13 | pub struct PhysicalPlanGenerator { 14 | pub(crate) _client_context: Arc, 15 | } 16 | 17 | impl PhysicalPlanGenerator { 18 | pub(crate) fn create_plan(&self, mut op: LogicalOperator) -> PhysicalOperator { 19 | // first resolve column references 20 | let mut resolver = ColumnBindingResolver::default(); 21 | resolver.visit_operator(&mut op); 22 | 23 | // now resolve types of all the operators 24 | op.resolve_operator_types(); 25 | 26 | // then create the main physical plan 27 | let plan = self.create_plan_internal(op); 28 | debug!( 29 | target: LOGGING_TARGET, 30 | "Physical Plan:\n{}", 31 | TreeRender::physical_plan_tree(&plan), 32 | ); 33 | plan 34 | } 35 | 36 | pub(crate) fn create_plan_internal(&self, op: LogicalOperator) -> PhysicalOperator { 37 | match op { 38 | LogicalOperator::LogicalCreateTable(op) => self.create_physical_create_table(op), 39 | LogicalOperator::LogicalExpressionGet(op) => self.create_physical_expression_scan(op), 40 | LogicalOperator::LogicalInsert(op) => self.create_physical_insert(op), 41 | LogicalOperator::LogicalGet(op) => self.create_physical_table_scan(op), 42 | LogicalOperator::LogicalProjection(op) => self.create_physical_projection(op), 43 | LogicalOperator::LogicalDummyScan(op) => self.create_physical_dummy_scan(op), 44 | LogicalOperator::LogicalExplain(op) => self.create_physical_explain(op), 45 | LogicalOperator::LogicalFilter(op) => self.create_physical_filter(op), 46 | LogicalOperator::LogicalLimit(op) => self.create_physical_limit(op), 47 | } 48 | } 49 | 50 | pub(crate) fn create_physical_operator_base( 51 | &self, 52 | base: LogicalOperatorBase, 53 | ) -> PhysicalOperatorBase { 54 | let children = base 55 | .children 56 | .into_iter() 57 | .map(|op| self.create_plan_internal(op)) 58 | .collect::>(); 59 | PhysicalOperatorBase::new(children, base.expressioins) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/execution/util.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use arrow::datatypes::{DataType, Field, Schema, SchemaRef}; 4 | use arrow::record_batch::RecordBatch; 5 | 6 | use super::ExecutorError; 7 | use crate::planner_v2::BoundExpression; 8 | use crate::types_v2::{LogicalType, ScalarValue}; 9 | 10 | pub struct SchemaUtil; 11 | 12 | impl SchemaUtil { 13 | pub fn new_schema_ref(names: &[String], types: &[LogicalType]) -> SchemaRef { 14 | let fields = names 15 | .iter() 16 | .zip(types.iter()) 17 | .map(|(name, ty)| Field::new(name, ty.clone().into(), true)) 18 | .collect::>(); 19 | SchemaRef::new(Schema::new_with_metadata(fields, HashMap::new())) 20 | } 21 | 22 | pub fn new_schema_ref_from_exprs(exprs: &[BoundExpression]) -> SchemaRef { 23 | let fields = exprs 24 | .iter() 25 | .map(|e| Field::new(&e.alias(), e.return_type().into(), true)) 26 | .collect::>(); 27 | SchemaRef::new(Schema::new_with_metadata(fields, HashMap::new())) 28 | } 29 | } 30 | 31 | pub struct RecordBatchUtil; 32 | 33 | impl RecordBatchUtil { 34 | pub fn new_one_row_dummy_batch() -> Result { 35 | let fields = vec![Field::new("dummy", DataType::Boolean, true)]; 36 | let schema = SchemaRef::new(Schema::new_with_metadata(fields, HashMap::new())); 37 | let array = ScalarValue::Boolean(Some(true)).to_array(); 38 | let batch = RecordBatch::try_new(schema, vec![array])?; 39 | Ok(batch) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/execution/volcano_executor/column_data_scan.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use arrow::record_batch::RecordBatch; 4 | use derive_new::new; 5 | use futures_async_stream::try_stream; 6 | 7 | use crate::execution::{ExecutionContext, ExecutorError, PhysicalColumnDataScan}; 8 | 9 | #[derive(new)] 10 | pub struct ColumnDataScan { 11 | pub(crate) plan: PhysicalColumnDataScan, 12 | } 13 | 14 | impl ColumnDataScan { 15 | #[try_stream(boxed, ok = RecordBatch, error = ExecutorError)] 16 | pub async fn execute(self, _context: Arc) { 17 | for batch in self.plan.collection.into_iter() { 18 | yield batch; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/execution/volcano_executor/create_table.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::sync::Arc; 3 | 4 | use arrow::array::StringArray; 5 | use arrow::datatypes::{DataType, Field, Schema, SchemaRef}; 6 | use arrow::record_batch::RecordBatch; 7 | use derive_new::new; 8 | use futures_async_stream::try_stream; 9 | 10 | use crate::catalog_v2::{Catalog, DataTable, DataTableInfo}; 11 | use crate::execution::{ExecutionContext, ExecutorError, PhysicalCreateTable}; 12 | use crate::planner_v2::BoundCreateTableInfo; 13 | use crate::storage_v2::LocalStorage; 14 | 15 | #[derive(new)] 16 | pub struct CreateTable { 17 | pub(crate) plan: PhysicalCreateTable, 18 | } 19 | 20 | impl CreateTable { 21 | pub fn create_table( 22 | context: Arc, 23 | info: &BoundCreateTableInfo, 24 | ) -> Result { 25 | let schema = info.base.base.schema.clone(); 26 | let table = info.base.table.clone(); 27 | let column_definitions = info.base.columns.clone(); 28 | let data_table = DataTable::new( 29 | DataTableInfo::new(schema.clone(), table.clone()), 30 | column_definitions, 31 | ); 32 | Catalog::create_table( 33 | context.clone_client_context(), 34 | schema, 35 | table, 36 | data_table.clone(), 37 | )?; 38 | LocalStorage::init_table(context.clone_client_context(), &data_table); 39 | Ok(data_table) 40 | } 41 | 42 | #[try_stream(boxed, ok = RecordBatch, error = ExecutorError)] 43 | pub async fn execute(self, context: Arc) { 44 | let table = self.plan.info.base.table.clone(); 45 | Self::create_table(context, &self.plan.info)?; 46 | let array = Arc::new(StringArray::from(vec![format!("CREATE TABLE {}", table)])); 47 | let fields = vec![Field::new("success", DataType::Utf8, false)]; 48 | yield RecordBatch::try_new( 49 | SchemaRef::new(Schema::new_with_metadata(fields, HashMap::new())), 50 | vec![array], 51 | )?; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/execution/volcano_executor/dummy_scan.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::sync::Arc; 3 | 4 | use arrow::datatypes::{DataType, Field, Schema, SchemaRef}; 5 | use arrow::record_batch::RecordBatch; 6 | use derive_new::new; 7 | use futures_async_stream::try_stream; 8 | 9 | use crate::execution::{ExecutionContext, ExecutorError, PhysicalDummyScan}; 10 | use crate::types_v2::ScalarValue; 11 | 12 | #[derive(new)] 13 | pub struct DummyScan { 14 | pub(crate) _plan: PhysicalDummyScan, 15 | } 16 | 17 | impl DummyScan { 18 | #[try_stream(boxed, ok = RecordBatch, error = ExecutorError)] 19 | pub async fn execute(self, _context: Arc) { 20 | let fields = vec![Field::new("dummy", DataType::Boolean, true)]; 21 | let schema = SchemaRef::new(Schema::new_with_metadata(fields, HashMap::new())); 22 | let array = ScalarValue::Boolean(Some(true)).to_array(); 23 | yield RecordBatch::try_new(schema.clone(), vec![array])?; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/execution/volcano_executor/expression_scan.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::sync::Arc; 3 | 4 | use arrow::datatypes::{Field, Schema, SchemaRef}; 5 | use arrow::record_batch::RecordBatch; 6 | use derive_new::new; 7 | use futures_async_stream::try_stream; 8 | 9 | use crate::execution::{ 10 | BoxedExecutor, ExecutionContext, ExecutorError, ExpressionExecutor, PhysicalExpressionScan, 11 | }; 12 | 13 | #[derive(new)] 14 | pub struct ExpressionScan { 15 | pub(crate) plan: PhysicalExpressionScan, 16 | pub(crate) child: BoxedExecutor, 17 | } 18 | 19 | impl ExpressionScan { 20 | #[try_stream(boxed, ok = RecordBatch, error = ExecutorError)] 21 | pub async fn execute(self, _context: Arc) { 22 | let mut fields = vec![]; 23 | for (idx, ty) in self.plan.expr_types.iter().enumerate() { 24 | fields.push(Field::new( 25 | format!("col{}", idx).as_str(), 26 | ty.clone().into(), 27 | true, 28 | )); 29 | } 30 | let schema = SchemaRef::new(Schema::new_with_metadata(fields, HashMap::new())); 31 | 32 | #[for_await] 33 | for batch in self.child { 34 | let input = batch?; 35 | for exprs in self.plan.expressions.iter() { 36 | let columns = ExpressionExecutor::execute(exprs, &input)?; 37 | yield RecordBatch::try_new(schema.clone(), columns)?; 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/execution/volcano_executor/filter.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use arrow::compute::filter_record_batch; 4 | use arrow::record_batch::RecordBatch; 5 | use derive_new::new; 6 | use futures_async_stream::try_stream; 7 | 8 | use crate::common::as_boolean_array; 9 | use crate::execution::{ 10 | BoxedExecutor, ExecutionContext, ExecutorError, ExpressionExecutor, PhysicalFilter, 11 | }; 12 | 13 | #[derive(new)] 14 | pub struct Filter { 15 | pub(crate) plan: PhysicalFilter, 16 | pub(crate) child: BoxedExecutor, 17 | } 18 | 19 | impl Filter { 20 | #[try_stream(boxed, ok = RecordBatch, error = ExecutorError)] 21 | pub async fn execute(self, _context: Arc) { 22 | let exprs = self.plan.base.expressioins; 23 | 24 | #[for_await] 25 | for batch in self.child { 26 | let batch = batch?; 27 | let eval_mask = ExpressionExecutor::execute(&exprs, &batch)?; 28 | let predicate = as_boolean_array(&eval_mask[0])?; 29 | yield filter_record_batch(&batch, predicate)?; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/execution/volcano_executor/projection.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use arrow::record_batch::RecordBatch; 4 | use derive_new::new; 5 | use futures_async_stream::try_stream; 6 | 7 | use crate::execution::{ 8 | BoxedExecutor, ExecutionContext, ExecutorError, ExpressionExecutor, PhysicalProjection, 9 | SchemaUtil, 10 | }; 11 | 12 | #[derive(new)] 13 | pub struct Projection { 14 | pub(crate) plan: PhysicalProjection, 15 | pub(crate) child: BoxedExecutor, 16 | } 17 | 18 | impl Projection { 19 | #[try_stream(boxed, ok = RecordBatch, error = ExecutorError)] 20 | pub async fn execute(self, _context: Arc) { 21 | let exprs = self.plan.base.expressioins; 22 | let schema = SchemaUtil::new_schema_ref_from_exprs(&exprs); 23 | 24 | #[for_await] 25 | for batch in self.child { 26 | let batch = batch?; 27 | let columns = ExpressionExecutor::execute(&exprs, &batch)?; 28 | yield RecordBatch::try_new(schema.clone(), columns)?; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/execution/volcano_executor/table_scan.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use arrow::record_batch::RecordBatch; 4 | use derive_new::new; 5 | use futures_async_stream::try_stream; 6 | 7 | use crate::execution::{ExecutionContext, ExecutorError, PhysicalTableScan, SchemaUtil}; 8 | use crate::function::TableFunctionInput; 9 | 10 | #[derive(new)] 11 | pub struct TableScan { 12 | pub(crate) plan: PhysicalTableScan, 13 | } 14 | 15 | impl TableScan { 16 | #[try_stream(boxed, ok = RecordBatch, error = ExecutorError)] 17 | pub async fn execute(self, context: Arc) { 18 | let schema = SchemaUtil::new_schema_ref(&self.plan.names, &self.plan.returned_types); 19 | 20 | let bind_data = self.plan.bind_data; 21 | 22 | let function = self.plan.function; 23 | let table_scan_func = function.function; 24 | let tabel_scan_input = TableFunctionInput::new(bind_data); 25 | 26 | let scan_stream = table_scan_func(context.clone_client_context(), tabel_scan_input)?; 27 | 28 | #[for_await] 29 | for batch in scan_stream { 30 | let batch = batch?; 31 | let columns = batch.columns().to_vec(); 32 | yield RecordBatch::try_new(schema.clone(), columns)? 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/executor/aggregate/count.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use ahash::RandomState; 4 | use arrow::array::ArrayRef; 5 | 6 | use super::Accumulator; 7 | use crate::executor::ExecutorError; 8 | use crate::types::ScalarValue; 9 | 10 | pub struct CountAccumulator { 11 | result: i64, 12 | } 13 | 14 | impl CountAccumulator { 15 | pub fn new() -> Self { 16 | Self { result: 0 } 17 | } 18 | } 19 | 20 | impl Accumulator for CountAccumulator { 21 | fn update_batch(&mut self, array: &ArrayRef) -> Result<(), ExecutorError> { 22 | self.result = (array.len() - array.null_count()) as i64; 23 | Ok(()) 24 | } 25 | 26 | fn evaluate(&self) -> Result { 27 | Ok(ScalarValue::Int64(Some(self.result))) 28 | } 29 | } 30 | 31 | pub struct DistinctCountAccumulator { 32 | distinct_values: HashSet, 33 | } 34 | 35 | impl DistinctCountAccumulator { 36 | pub fn new() -> Self { 37 | Self { 38 | distinct_values: HashSet::default(), 39 | } 40 | } 41 | } 42 | 43 | impl Accumulator for DistinctCountAccumulator { 44 | fn update_batch(&mut self, array: &ArrayRef) -> Result<(), ExecutorError> { 45 | if array.is_empty() { 46 | return Ok(()); 47 | } 48 | (0..array.len()).for_each(|i| { 49 | let v = ScalarValue::try_from_array(array, i); 50 | self.distinct_values.insert(v); 51 | }); 52 | Ok(()) 53 | } 54 | 55 | fn evaluate(&self) -> Result { 56 | Ok(ScalarValue::Int64(Some(self.distinct_values.len() as i64))) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/executor/aggregate/mod.rs: -------------------------------------------------------------------------------- 1 | use arrow::array::ArrayRef; 2 | 3 | use self::count::{CountAccumulator, DistinctCountAccumulator}; 4 | use self::min_max::{MaxAccumulator, MinAccumulator}; 5 | use self::sum::{DistinctSumAccumulator, SumAccumulator}; 6 | use super::ExecutorError; 7 | use crate::binder::{AggFunc, BoundExpr}; 8 | use crate::types::ScalarValue; 9 | 10 | mod count; 11 | pub mod hash_agg; 12 | pub mod hash_utils; 13 | mod min_max; 14 | pub mod simple_agg; 15 | mod sum; 16 | 17 | /// An accumulator represents a stateful object that lives throughout the evaluation of multiple 18 | /// rows and generically accumulates values. 19 | pub trait Accumulator: Send + Sync { 20 | /// updates the accumulator's state from a vector of arrays. 21 | fn update_batch(&mut self, array: &ArrayRef) -> Result<(), ExecutorError>; 22 | 23 | /// returns its value based on its current state. 24 | fn evaluate(&self) -> Result; 25 | } 26 | 27 | fn create_accumulator(expr: &BoundExpr) -> Box { 28 | if let BoundExpr::AggFunc(agg_expr) = expr { 29 | match (&agg_expr.func, &agg_expr.distinct) { 30 | (AggFunc::Count, false) => Box::new(CountAccumulator::new()), 31 | (AggFunc::Count, true) => Box::new(DistinctCountAccumulator::new()), 32 | (AggFunc::Sum, false) => Box::new(SumAccumulator::new(agg_expr.return_type.clone())), 33 | (AggFunc::Sum, true) => { 34 | Box::new(DistinctSumAccumulator::new(agg_expr.return_type.clone())) 35 | } 36 | (AggFunc::Min, _) => Box::new(MinAccumulator::new(agg_expr.return_type.clone())), 37 | (AggFunc::Max, _) => Box::new(MaxAccumulator::new(agg_expr.return_type.clone())), 38 | } 39 | } else { 40 | unreachable!( 41 | "create_accumulator called with non-aggregate expression {:?}", 42 | expr 43 | ); 44 | } 45 | } 46 | 47 | fn create_accumulators(exprs: &[BoundExpr]) -> Vec> { 48 | exprs.iter().map(create_accumulator).collect() 49 | } 50 | -------------------------------------------------------------------------------- /src/executor/aggregate/simple_agg.rs: -------------------------------------------------------------------------------- 1 | use arrow::array::ArrayRef; 2 | use arrow::datatypes::{Field, Schema, SchemaRef}; 3 | use itertools::Itertools; 4 | 5 | use super::create_accumulators; 6 | use crate::binder::{BoundAggFunc, BoundExpr}; 7 | use crate::executor::*; 8 | use crate::types::build_scalar_value_array; 9 | 10 | pub struct SimpleAggExecutor { 11 | pub agg_funcs: Vec, 12 | pub child: BoxedExecutor, 13 | } 14 | 15 | impl SimpleAggExecutor { 16 | fn cast_agg_funcs(&self) -> Vec { 17 | self.agg_funcs 18 | .iter() 19 | .map(|e| match e { 20 | BoundExpr::AggFunc(agg) => agg.clone(), 21 | _ => unreachable!(""), 22 | }) 23 | .collect_vec() 24 | } 25 | 26 | #[try_stream(boxed, ok = RecordBatch, error = ExecutorError)] 27 | pub async fn execute(self) { 28 | let mut accs = create_accumulators(&self.agg_funcs); 29 | let agg_funcs = self.cast_agg_funcs(); 30 | 31 | let mut agg_fileds: Option> = None; 32 | 33 | #[for_await] 34 | for batch in self.child { 35 | let batch = batch?; 36 | // only support one epxrssion in aggregation, not supported example: `sum(distinct a)` 37 | let columns: Result, ExecutorError> = agg_funcs 38 | .iter() 39 | .map(|agg| agg.exprs[0].eval_column(&batch)) 40 | .try_collect(); 41 | 42 | // build new schema for aggregation result 43 | if agg_fileds.is_none() { 44 | agg_fileds = Some( 45 | self.agg_funcs 46 | .iter() 47 | .map(|agg| agg.eval_field(&batch)) 48 | .collect(), 49 | ); 50 | } 51 | let columns = columns?; 52 | for (acc, column) in accs.iter_mut().zip_eq(columns.iter()) { 53 | acc.update_batch(column)?; 54 | } 55 | } 56 | 57 | let mut columns: Vec = Vec::new(); 58 | for acc in accs.iter() { 59 | let res = acc.evaluate()?; 60 | columns.push(build_scalar_value_array(&res, 1)); 61 | } 62 | 63 | let schema = SchemaRef::new(Schema::new(agg_fileds.unwrap())); 64 | yield RecordBatch::try_new(schema, columns)?; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/executor/filter.rs: -------------------------------------------------------------------------------- 1 | use arrow::array::BooleanArray; 2 | use arrow::compute::filter_record_batch; 3 | 4 | use super::*; 5 | use crate::binder::BoundExpr; 6 | 7 | pub struct FilterExecutor { 8 | pub expr: BoundExpr, 9 | pub child: BoxedExecutor, 10 | } 11 | 12 | impl FilterExecutor { 13 | #[try_stream(boxed, ok = RecordBatch, error = ExecutorError)] 14 | pub async fn execute(self) { 15 | #[for_await] 16 | for batch in self.child { 17 | let batch = batch?; 18 | let eval_mask = self.expr.eval_column(&batch)?; 19 | let predicate = eval_mask 20 | .as_any() 21 | .downcast_ref::() 22 | .expect("filter executor expected evaluate boolean array"); 23 | let batch = filter_record_batch(&batch, predicate)?; 24 | yield batch; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/executor/join/cross_join.rs: -------------------------------------------------------------------------------- 1 | use arrow::compute::concat_batches; 2 | use arrow::datatypes::{Schema, SchemaRef}; 3 | 4 | use crate::catalog::ColumnCatalog; 5 | use crate::executor::*; 6 | use crate::types::{build_scalar_value_array, ScalarValue}; 7 | 8 | pub struct CrossJoinExecutor { 9 | pub left_child: BoxedExecutor, 10 | pub right_child: BoxedExecutor, 11 | /// The schema once the join is applied 12 | pub join_output_schema: Vec, 13 | } 14 | 15 | impl CrossJoinExecutor { 16 | fn join_output_arrow_schema(&self) -> SchemaRef { 17 | let fields = self 18 | .join_output_schema 19 | .iter() 20 | .map(|c| c.to_arrow_field()) 21 | .collect::>(); 22 | SchemaRef::new(Schema::new(fields)) 23 | } 24 | 25 | #[try_stream(boxed, ok = RecordBatch, error = ExecutorError)] 26 | pub async fn execute(self) { 27 | let schema = self.join_output_arrow_schema(); 28 | 29 | // consume all left stream data and then iterate right stream chunk to build result 30 | let left_batches = self.left_child.try_collect::>().await?; 31 | 32 | if left_batches.is_empty() { 33 | return Ok(()); 34 | } 35 | 36 | let left_single_batch = concat_batches(&left_batches[0].schema(), &left_batches)?; 37 | 38 | #[for_await] 39 | for right_batch in self.right_child { 40 | let right_data = right_batch?; 41 | 42 | // repeat left value n times to match right batch size 43 | for row_idx in 0..left_single_batch.num_rows() { 44 | let new_left_data = left_single_batch 45 | .columns() 46 | .iter() 47 | .map(|col_arr| { 48 | let scalar = ScalarValue::try_from_array(col_arr, row_idx); 49 | build_scalar_value_array(&scalar, right_data.num_rows()) 50 | }) 51 | .collect::>(); 52 | // concat left and right data 53 | let data = vec![new_left_data, right_data.columns().to_vec()].concat(); 54 | yield RecordBatch::try_new(schema.clone(), data)? 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/executor/join/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cross_join; 2 | pub mod hash_join; 3 | -------------------------------------------------------------------------------- /src/executor/order.rs: -------------------------------------------------------------------------------- 1 | use arrow::compute::{ 2 | concat_batches, lexsort_to_indices, take, SortColumn, SortOptions, TakeOptions, 3 | }; 4 | 5 | use super::*; 6 | use crate::binder::BoundOrderBy; 7 | 8 | pub struct OrderExecutor { 9 | pub order_by: Vec, 10 | pub child: BoxedExecutor, 11 | } 12 | 13 | impl OrderExecutor { 14 | #[try_stream(boxed, ok = RecordBatch, error = ExecutorError)] 15 | pub async fn execute(self) { 16 | let mut schema = None; 17 | let mut batches = vec![]; 18 | #[for_await] 19 | for batch in self.child { 20 | let batch = batch?; 21 | if schema.is_none() { 22 | schema = Some(batch.schema()); 23 | } 24 | batches.push(batch); 25 | } 26 | 27 | let schema = schema.unwrap(); 28 | let batch = concat_batches(&schema, &batches)?; 29 | 30 | let sort_columns = self 31 | .order_by 32 | .iter() 33 | .map(|expr| -> Result { 34 | let sort_array = expr.expr.eval_column(&batch)?; 35 | Ok(SortColumn { 36 | values: sort_array, 37 | options: Some(SortOptions { 38 | descending: !expr.asc, 39 | ..Default::default() 40 | }), 41 | }) 42 | }) 43 | .try_collect::>()?; 44 | 45 | let indices = lexsort_to_indices(&sort_columns, None)?; 46 | 47 | let sorted_batch = RecordBatch::try_new( 48 | schema, 49 | batch 50 | .columns() 51 | .iter() 52 | .map(|column| { 53 | take( 54 | column.as_ref(), 55 | &indices, 56 | // disable bound check overhead since indices are already generated from 57 | // the same record batch 58 | Some(TakeOptions { 59 | check_bounds: false, 60 | }), 61 | ) 62 | }) 63 | .try_collect::>()?, 64 | )?; 65 | 66 | yield sorted_batch; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/executor/project.rs: -------------------------------------------------------------------------------- 1 | use arrow::datatypes::{Schema, SchemaRef}; 2 | use arrow::record_batch::RecordBatch; 3 | 4 | use super::*; 5 | use crate::binder::BoundExpr; 6 | pub struct ProjectExecutor { 7 | pub exprs: Vec, 8 | pub child: BoxedExecutor, 9 | } 10 | 11 | impl ProjectExecutor { 12 | #[try_stream(boxed, ok = RecordBatch, error = ExecutorError)] 13 | pub async fn execute(self) { 14 | #[for_await] 15 | for batch in self.child { 16 | let batch = batch?; 17 | let columns = self 18 | .exprs 19 | .iter() 20 | .map(|e| e.eval_column(&batch)) 21 | .try_collect(); 22 | let fields = self.exprs.iter().map(|e| e.eval_field(&batch)).collect(); 23 | let schema = SchemaRef::new(Schema::new_with_metadata( 24 | fields, 25 | batch.schema().metadata().clone(), 26 | )); 27 | yield RecordBatch::try_new(schema, columns?)?; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/executor/table_scan.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use arrow::record_batch::RecordBatch; 4 | 5 | use super::*; 6 | use crate::optimizer::PhysicalTableScan; 7 | use crate::storage::{Storage, Table, Transaction}; 8 | 9 | pub struct TableScanExecutor { 10 | pub plan: PhysicalTableScan, 11 | pub storage: Arc, 12 | } 13 | 14 | impl TableScanExecutor { 15 | #[try_stream(boxed, ok = RecordBatch, error = ExecutorError)] 16 | pub async fn execute(self) { 17 | let table_id = self.plan.logical().table_id(); 18 | let table = self.storage.get_table(table_id)?; 19 | let mut tx = table.read( 20 | self.plan.logical().bounds(), 21 | self.plan.logical().projections(), 22 | )?; 23 | loop { 24 | match tx.next_batch() { 25 | Ok(batch) => { 26 | if let Some(batch) = batch { 27 | yield batch; 28 | } else { 29 | break; 30 | } 31 | } 32 | Err(err) => return Err(err.into()), 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/function/cast/cast_function.rs: -------------------------------------------------------------------------------- 1 | use arrow::array::ArrayRef; 2 | use derive_new::new; 3 | 4 | use crate::function::FunctionError; 5 | use crate::types_v2::LogicalType; 6 | 7 | pub type CastFunc = 8 | fn(array: &ArrayRef, to_type: &LogicalType, try_cast: bool) -> Result; 9 | 10 | #[derive(new, Clone)] 11 | pub struct CastFunction { 12 | /// The source type of the cast 13 | pub(crate) source: LogicalType, 14 | /// The target type of the cast 15 | pub(crate) target: LogicalType, 16 | /// The main cast function to execute 17 | pub(crate) function: CastFunc, 18 | } 19 | 20 | impl std::fmt::Debug for CastFunction { 21 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 22 | f.debug_struct("CastFunction") 23 | .field("cast", &format!("{:?} -> {:?}", self.source, self.target)) 24 | .finish() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/function/cast/cast_rules.rs: -------------------------------------------------------------------------------- 1 | use crate::types_v2::LogicalType; 2 | 3 | pub struct CastRules; 4 | 5 | impl CastRules { 6 | pub fn implicit_cast_cost(from: &LogicalType, to: &LogicalType) -> i32 { 7 | if from == to { 8 | 0 9 | } else if LogicalType::can_implicit_cast(from, to) { 10 | 1 11 | } else { 12 | -1 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/function/cast/default_cast.rs: -------------------------------------------------------------------------------- 1 | use arrow::array::ArrayRef; 2 | use arrow::compute::{cast_with_options, CastOptions}; 3 | 4 | use super::CastFunction; 5 | use crate::function::FunctionError; 6 | use crate::types_v2::LogicalType; 7 | 8 | pub struct DefaultCastFunctions; 9 | 10 | impl DefaultCastFunctions { 11 | fn default_cast_function( 12 | array: &ArrayRef, 13 | to_type: &LogicalType, 14 | try_cast: bool, 15 | ) -> Result { 16 | let to_type = to_type.clone().into(); 17 | let options = CastOptions { safe: try_cast }; 18 | Ok(cast_with_options(array, &to_type, &options)?) 19 | } 20 | 21 | pub fn get_cast_function( 22 | source: &LogicalType, 23 | target: &LogicalType, 24 | ) -> Result { 25 | assert!(source != target); 26 | match source { 27 | LogicalType::Invalid => { 28 | Err(FunctionError::CastError("Invalid source type".to_string())) 29 | } 30 | _ => Ok(CastFunction::new( 31 | source.clone(), 32 | target.clone(), 33 | Self::default_cast_function, 34 | )), 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/function/cast/mod.rs: -------------------------------------------------------------------------------- 1 | mod cast_function; 2 | mod cast_rules; 3 | mod default_cast; 4 | 5 | pub use cast_function::*; 6 | pub use cast_rules::*; 7 | pub use default_cast::*; 8 | -------------------------------------------------------------------------------- /src/function/comparison/comparison_function.rs: -------------------------------------------------------------------------------- 1 | use arrow::array::ArrayRef; 2 | use derive_new::new; 3 | 4 | use crate::function::FunctionError; 5 | use crate::types_v2::LogicalType; 6 | 7 | pub type ComparisonFunc = fn(left: &ArrayRef, right: &ArrayRef) -> Result; 8 | 9 | #[derive(new, Clone)] 10 | pub struct ComparisonFunction { 11 | // The name of the function 12 | pub(crate) name: String, 13 | /// The main comparision function to execute. 14 | /// Left and right arguments must be the same type 15 | pub(crate) function: ComparisonFunc, 16 | /// The comparison type 17 | pub(crate) comparison_type: LogicalType, 18 | } 19 | 20 | impl std::fmt::Debug for ComparisonFunction { 21 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 22 | f.debug_struct("CompressionFunction") 23 | .field("name", &self.name) 24 | .field( 25 | "func", 26 | &format!( 27 | "{}{}{}", 28 | self.comparison_type, self.name, self.comparison_type 29 | ), 30 | ) 31 | .finish() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/function/comparison/default_comparison.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use arrow::array::ArrayRef; 4 | use arrow::compute::{eq_dyn, gt_dyn, gt_eq_dyn, lt_dyn, lt_eq_dyn, neq_dyn}; 5 | use sqlparser::ast::BinaryOperator; 6 | 7 | use super::{ComparisonFunc, ComparisonFunction}; 8 | use crate::function::FunctionError; 9 | use crate::types_v2::LogicalType; 10 | 11 | pub struct DefaultComparisonFunctions; 12 | 13 | impl DefaultComparisonFunctions { 14 | fn default_gt_function(left: &ArrayRef, right: &ArrayRef) -> Result { 15 | Ok(Arc::new(gt_dyn(left, right)?)) 16 | } 17 | 18 | fn default_gt_eq_function( 19 | left: &ArrayRef, 20 | right: &ArrayRef, 21 | ) -> Result { 22 | Ok(Arc::new(gt_eq_dyn(left, right)?)) 23 | } 24 | 25 | fn default_lt_function(left: &ArrayRef, right: &ArrayRef) -> Result { 26 | Ok(Arc::new(lt_dyn(left, right)?)) 27 | } 28 | 29 | fn default_lt_eq_function( 30 | left: &ArrayRef, 31 | right: &ArrayRef, 32 | ) -> Result { 33 | Ok(Arc::new(lt_eq_dyn(left, right)?)) 34 | } 35 | 36 | fn default_eq_function(left: &ArrayRef, right: &ArrayRef) -> Result { 37 | Ok(Arc::new(eq_dyn(left, right)?)) 38 | } 39 | 40 | fn default_neq_function(left: &ArrayRef, right: &ArrayRef) -> Result { 41 | Ok(Arc::new(neq_dyn(left, right)?)) 42 | } 43 | 44 | fn get_comparison_function_internal( 45 | op: &BinaryOperator, 46 | ) -> Result<(&str, ComparisonFunc), FunctionError> { 47 | Ok(match op { 48 | BinaryOperator::Eq => ("eq", Self::default_eq_function), 49 | BinaryOperator::NotEq => ("neq", Self::default_neq_function), 50 | BinaryOperator::Lt => ("lt", Self::default_lt_function), 51 | BinaryOperator::LtEq => ("lt_eq", Self::default_lt_eq_function), 52 | BinaryOperator::Gt => ("gt", Self::default_gt_function), 53 | BinaryOperator::GtEq => ("gt_eq", Self::default_gt_eq_function), 54 | _ => { 55 | return Err(FunctionError::ComparisonError(format!( 56 | "Unsupported comparison operator {:?}", 57 | op 58 | ))) 59 | } 60 | }) 61 | } 62 | 63 | pub fn get_comparison_function( 64 | op: &BinaryOperator, 65 | comparison_type: &LogicalType, 66 | ) -> Result { 67 | if comparison_type == &LogicalType::Invalid { 68 | return Err(FunctionError::ComparisonError( 69 | "Invalid comparison type".to_string(), 70 | )); 71 | } 72 | let (name, func) = Self::get_comparison_function_internal(op)?; 73 | Ok(ComparisonFunction::new( 74 | name.to_string(), 75 | func, 76 | comparison_type.clone(), 77 | )) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/function/comparison/mod.rs: -------------------------------------------------------------------------------- 1 | mod comparison_function; 2 | mod default_comparison; 3 | pub use comparison_function::*; 4 | pub use default_comparison::*; 5 | -------------------------------------------------------------------------------- /src/function/conjunction/conjunction_function.rs: -------------------------------------------------------------------------------- 1 | use arrow::array::ArrayRef; 2 | use derive_new::new; 3 | use strum_macros::AsRefStr; 4 | 5 | use crate::function::FunctionError; 6 | 7 | pub type ConjunctionFunc = fn(left: &ArrayRef, right: &ArrayRef) -> Result; 8 | 9 | #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, AsRefStr)] 10 | pub enum ConjunctionType { 11 | And, 12 | Or, 13 | } 14 | 15 | #[derive(new, Clone)] 16 | pub struct ConjunctionFunction { 17 | pub(crate) name: String, 18 | pub(crate) function: ConjunctionFunc, 19 | pub(crate) ty: ConjunctionType, 20 | } 21 | 22 | impl std::fmt::Debug for ConjunctionFunction { 23 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 24 | f.debug_struct("ConjunctionFunction") 25 | .field("name", &self.name) 26 | .finish() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/function/conjunction/default_conjunction.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use arrow::array::{ArrayRef, BooleanArray}; 4 | use arrow::compute::{and_kleene, or_kleene}; 5 | use arrow::datatypes::DataType; 6 | use sqlparser::ast::BinaryOperator; 7 | 8 | use super::{ConjunctionFunc, ConjunctionFunction, ConjunctionType}; 9 | use crate::function::FunctionError; 10 | 11 | pub struct DefaultConjunctionFunctions; 12 | 13 | macro_rules! boolean_op { 14 | ($LEFT:expr, $RIGHT:expr, $OP:ident) => {{ 15 | if *$LEFT.data_type() != DataType::Boolean || *$RIGHT.data_type() != DataType::Boolean { 16 | return Err(FunctionError::ConjunctionError(format!( 17 | "Cannot evaluate binary expression with types {:?} and {:?}, only Boolean supported", 18 | $LEFT.data_type(), 19 | $RIGHT.data_type() 20 | ))); 21 | } 22 | 23 | let ll = $LEFT 24 | .as_any() 25 | .downcast_ref::() 26 | .expect("boolean_op failed to downcast array"); 27 | let rr = $RIGHT 28 | .as_any() 29 | .downcast_ref::() 30 | .expect("boolean_op failed to downcast array"); 31 | Ok(Arc::new($OP(&ll, &rr)?)) 32 | }}; 33 | } 34 | 35 | impl DefaultConjunctionFunctions { 36 | fn default_and_function(left: &ArrayRef, right: &ArrayRef) -> Result { 37 | boolean_op!(left, right, and_kleene) 38 | } 39 | 40 | fn default_or_function(left: &ArrayRef, right: &ArrayRef) -> Result { 41 | boolean_op!(left, right, or_kleene) 42 | } 43 | 44 | fn get_conjunction_function_internal( 45 | op: &BinaryOperator, 46 | ) -> Result<(ConjunctionType, ConjunctionFunc), FunctionError> { 47 | Ok(match op { 48 | BinaryOperator::And => (ConjunctionType::And, Self::default_and_function), 49 | BinaryOperator::Or => (ConjunctionType::Or, Self::default_or_function), 50 | _ => { 51 | return Err(FunctionError::ConjunctionError(format!( 52 | "Unsupported conjunction operator {:?}", 53 | op 54 | ))) 55 | } 56 | }) 57 | } 58 | 59 | pub fn get_conjunction_function( 60 | op: &BinaryOperator, 61 | ) -> Result { 62 | let (ty, func) = Self::get_conjunction_function_internal(op)?; 63 | Ok(ConjunctionFunction::new(ty.as_ref().to_string(), func, ty)) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/function/conjunction/mod.rs: -------------------------------------------------------------------------------- 1 | mod conjunction_function; 2 | mod default_conjunction; 3 | pub use conjunction_function::*; 4 | pub use default_conjunction::*; 5 | -------------------------------------------------------------------------------- /src/function/errors.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use arrow::error::ArrowError; 4 | 5 | use crate::catalog_v2::CatalogError; 6 | use crate::planner_v2::BindError; 7 | use crate::types_v2::TypeError; 8 | 9 | pub type FunctionResult = Result; 10 | 11 | // TODO: refactor error using https://docs.rs/snafu/latest/snafu/ 12 | #[derive(thiserror::Error, Debug)] 13 | pub enum FunctionError { 14 | #[error("catalog error: {0}")] 15 | CatalogError( 16 | #[from] 17 | #[source] 18 | CatalogError, 19 | ), 20 | #[error("type error: {0}")] 21 | TypeError( 22 | #[from] 23 | #[source] 24 | TypeError, 25 | ), 26 | #[error("arrow error: {0}")] 27 | ArrowError( 28 | #[from] 29 | #[source] 30 | ArrowError, 31 | ), 32 | #[error("Internal error: {0}")] 33 | InternalError(String), 34 | #[error("Cast error: {0}")] 35 | CastError(String), 36 | #[error("Comparison error: {0}")] 37 | ComparisonError(String), 38 | #[error("Conjunction error: {0}")] 39 | ConjunctionError(String), 40 | #[error("io error")] 41 | IoError(#[from] io::Error), 42 | } 43 | 44 | impl From for FunctionError { 45 | fn from(e: BindError) -> Self { 46 | FunctionError::InternalError(e.to_string()) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/function/mod.rs: -------------------------------------------------------------------------------- 1 | mod cast; 2 | mod comparison; 3 | mod conjunction; 4 | mod errors; 5 | mod scalar; 6 | mod table; 7 | 8 | use std::sync::Arc; 9 | 10 | pub use cast::*; 11 | pub use comparison::*; 12 | pub use conjunction::*; 13 | use derive_new::new; 14 | pub use errors::*; 15 | pub use scalar::*; 16 | pub use table::*; 17 | 18 | use crate::catalog_v2::{Catalog, DEFAULT_SCHEMA}; 19 | use crate::common::{CreateInfoBase, CreateScalarFunctionInfo, CreateTableFunctionInfo}; 20 | use crate::main_entry::ClientContext; 21 | 22 | #[derive(Debug, Clone)] 23 | pub enum FunctionData { 24 | SeqTableScanInputData(Box), 25 | SqlrsTablesData(Box), 26 | SqlrsColumnsData(Box), 27 | ReadCSVInputData(Box), 28 | } 29 | 30 | #[derive(new)] 31 | pub struct BuiltinFunctions { 32 | pub(crate) context: Arc, 33 | } 34 | 35 | impl BuiltinFunctions { 36 | pub fn add_table_functions(&mut self, function: TableFunction) -> Result<(), FunctionError> { 37 | let info = CreateTableFunctionInfo::new( 38 | CreateInfoBase::new(DEFAULT_SCHEMA.to_string()), 39 | function.name.clone(), 40 | vec![function], 41 | ); 42 | Ok(Catalog::create_table_function(self.context.clone(), info)?) 43 | } 44 | 45 | pub fn add_scalar_functions( 46 | &mut self, 47 | function_name: String, 48 | functions: Vec, 49 | ) -> Result<(), FunctionError> { 50 | let info = CreateScalarFunctionInfo::new( 51 | CreateInfoBase::new(DEFAULT_SCHEMA.to_string()), 52 | function_name, 53 | functions, 54 | ); 55 | Ok(Catalog::create_scalar_function(self.context.clone(), info)?) 56 | } 57 | 58 | pub fn initialize(&mut self) -> Result<(), FunctionError> { 59 | SqlrsTablesFunc::register_function(self)?; 60 | SqlrsColumnsFunc::register_function(self)?; 61 | AddFunction::register_function(self)?; 62 | SubtractFunction::register_function(self)?; 63 | MultiplyFunction::register_function(self)?; 64 | DivideFunction::register_function(self)?; 65 | ReadCSV::register_function(self)?; 66 | Ok(()) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/function/scalar/mod.rs: -------------------------------------------------------------------------------- 1 | mod arithmetic_function; 2 | mod scalar_function; 3 | pub use arithmetic_function::*; 4 | pub use scalar_function::*; 5 | -------------------------------------------------------------------------------- /src/function/scalar/scalar_function.rs: -------------------------------------------------------------------------------- 1 | use arrow::array::ArrayRef; 2 | use derive_new::new; 3 | 4 | use crate::function::FunctionError; 5 | use crate::types_v2::LogicalType; 6 | 7 | pub type ScalarFunc = fn(inputs: &[ArrayRef]) -> Result; 8 | 9 | #[derive(new, Clone)] 10 | pub struct ScalarFunction { 11 | // The name of the function 12 | pub(crate) name: String, 13 | /// The main scalar function to execute 14 | pub(crate) function: ScalarFunc, 15 | /// The set of arguments of the function 16 | pub(crate) arguments: Vec, 17 | /// Return type of the function 18 | pub(crate) return_type: LogicalType, 19 | } 20 | 21 | impl std::fmt::Debug for ScalarFunction { 22 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 23 | f.debug_struct("ScalarFunction") 24 | .field("name", &self.name) 25 | .field( 26 | "types", 27 | &format!("{:?} -> {:?}", self.arguments, self.return_type), 28 | ) 29 | .finish() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/function/table/mod.rs: -------------------------------------------------------------------------------- 1 | mod read_csv; 2 | mod seq_table_scan; 3 | mod sqlrs_columns; 4 | mod sqlrs_tables; 5 | mod table_function; 6 | pub use read_csv::*; 7 | pub use seq_table_scan::*; 8 | pub use sqlrs_columns::*; 9 | pub use sqlrs_tables::*; 10 | pub use table_function::*; 11 | -------------------------------------------------------------------------------- /src/function/table/seq_table_scan.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use arrow::record_batch::RecordBatch; 4 | use derive_new::new; 5 | use futures::stream::BoxStream; 6 | 7 | use super::{TableFunction, TableFunctionBindInput, TableFunctionInput}; 8 | use crate::catalog_v2::TableCatalogEntry; 9 | use crate::function::{FunctionData, FunctionError, FunctionResult}; 10 | use crate::main_entry::ClientContext; 11 | use crate::storage_v2::LocalStorage; 12 | use crate::types_v2::LogicalType; 13 | 14 | /// The table scan function represents a sequential scan over one of base tables. 15 | pub struct SeqTableScan; 16 | 17 | #[derive(new, Debug, Clone)] 18 | pub struct SeqTableScanInputData { 19 | pub(crate) bind_table: TableCatalogEntry, 20 | } 21 | 22 | impl SeqTableScan { 23 | #[allow(clippy::ptr_arg)] 24 | fn bind_func( 25 | _context: Arc, 26 | input: TableFunctionBindInput, 27 | _return_types: &mut Vec, 28 | _return_names: &mut Vec, 29 | ) -> FunctionResult> { 30 | if let Some(table) = input.bind_table { 31 | let res = 32 | FunctionData::SeqTableScanInputData(Box::new(SeqTableScanInputData::new(table))); 33 | Ok(Some(res)) 34 | } else { 35 | Err(FunctionError::InternalError( 36 | "unexpected bind data type".to_string(), 37 | )) 38 | } 39 | } 40 | 41 | fn scan_func( 42 | context: Arc, 43 | input: TableFunctionInput, 44 | ) -> FunctionResult>> { 45 | if let Some(FunctionData::SeqTableScanInputData(data)) = input.bind_data { 46 | let mut reader = LocalStorage::create_reader(&data.bind_table.storage); 47 | let stream = Box::pin(async_stream::try_stream! { 48 | while let Some(batch) = reader.next_batch(context.clone()){ 49 | yield batch; 50 | } 51 | }); 52 | Ok(stream) 53 | } else { 54 | Err(FunctionError::InternalError( 55 | "unexpected bind data type".to_string(), 56 | )) 57 | } 58 | } 59 | 60 | pub fn get_function() -> TableFunction { 61 | TableFunction::new( 62 | "seq_table_scan".to_string(), 63 | Some(Self::bind_func), 64 | Self::scan_func, 65 | ) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/function/table/table_function.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::sync::Arc; 3 | 4 | use arrow::record_batch::RecordBatch; 5 | use derive_new::new; 6 | use futures::stream::BoxStream; 7 | use sqlparser::ast::FunctionArg; 8 | 9 | use crate::catalog_v2::TableCatalogEntry; 10 | use crate::function::{FunctionData, FunctionResult}; 11 | use crate::main_entry::ClientContext; 12 | use crate::types_v2::LogicalType; 13 | 14 | #[derive(new, Default)] 15 | pub struct TableFunctionBindInput { 16 | pub(crate) bind_table: Option, 17 | #[allow(dead_code)] 18 | pub(crate) func_args: Option>, 19 | } 20 | 21 | #[derive(new, Default)] 22 | pub struct TableFunctionInput { 23 | pub(crate) bind_data: Option, 24 | } 25 | 26 | pub type TableFunctionBindFunc = fn( 27 | Arc, 28 | TableFunctionBindInput, 29 | &mut Vec, 30 | &mut Vec, 31 | ) -> FunctionResult>; 32 | 33 | pub type TableFunc = fn( 34 | Arc, 35 | TableFunctionInput, 36 | ) -> FunctionResult>>; 37 | 38 | #[derive(new, Clone)] 39 | pub struct TableFunction { 40 | // The name of the function 41 | pub(crate) name: String, 42 | /// Bind function 43 | /// This function is used for determining the return type of a table producing function and 44 | /// returning bind data The returned FunctionData object should be constant and should not 45 | /// be changed during execution. 46 | pub(crate) bind: Option, 47 | /// The main function 48 | pub(crate) function: TableFunc, 49 | } 50 | 51 | impl Debug for TableFunction { 52 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 53 | f.debug_struct("TableFunction") 54 | .field("name", &self.name) 55 | .finish() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(generators, proc_macro_hygiene, stmt_expr_attributes)] 2 | #![feature(generic_associated_types)] 3 | #![feature(backtrace)] 4 | #![feature(iterator_try_collect)] 5 | #![feature(assert_matches)] 6 | 7 | #[macro_use] 8 | extern crate lazy_static; 9 | 10 | pub mod binder; 11 | pub mod catalog; 12 | pub mod catalog_v2; 13 | pub mod cli; 14 | pub mod common; 15 | pub mod db; 16 | pub mod execution; 17 | pub mod executor; 18 | pub mod function; 19 | pub mod main_entry; 20 | pub mod optimizer; 21 | pub mod parser; 22 | pub mod planner; 23 | pub mod planner_v2; 24 | pub mod storage; 25 | pub mod storage_v2; 26 | pub mod types; 27 | pub mod types_v2; 28 | pub mod util; 29 | 30 | pub use self::db::{Database, DatabaseError}; 31 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use anyhow::Result; 4 | use sqlrs::main_entry::{ClientContext, DatabaseInstance}; 5 | use sqlrs::{cli, Database}; 6 | 7 | #[tokio::main] 8 | async fn main() -> Result<()> { 9 | env_logger::init(); 10 | 11 | let db = Database::new_on_csv(); 12 | create_csv_table(&db, "employee")?; 13 | create_csv_table(&db, "department")?; 14 | create_csv_table(&db, "state")?; 15 | create_csv_table(&db, "t1")?; 16 | create_csv_table(&db, "t2")?; 17 | 18 | let dbv2 = Arc::new(DatabaseInstance::default()); 19 | dbv2.initialize()?; 20 | let client_context = ClientContext::new(dbv2); 21 | cli::interactive(db, client_context).await?; 22 | 23 | Ok(()) 24 | } 25 | 26 | fn create_csv_table(db: &Database, table_name: &str) -> Result<()> { 27 | let table_name = table_name.to_string(); 28 | let filepath = format!("./tests/csv/{}.csv", table_name); 29 | db.create_csv_table(table_name, filepath)?; 30 | 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /src/main_entry/db.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, RwLock}; 2 | 3 | use super::{ClientContext, DatabaseError}; 4 | use crate::catalog_v2::{Catalog, CatalogError, DEFAULT_SCHEMA}; 5 | use crate::function::BuiltinFunctions; 6 | use crate::storage_v2::LocalStorage; 7 | 8 | #[derive(Default)] 9 | pub struct DatabaseInstance { 10 | pub(crate) storage: RwLock, 11 | pub(crate) catalog: Arc>, 12 | } 13 | 14 | impl DatabaseInstance { 15 | pub fn initialize(self: &Arc) -> Result<(), DatabaseError> { 16 | // Create the default schema: main 17 | self.init_default_schema()?; 18 | // Initialize the builtin functions 19 | self.init_builtin_functions()?; 20 | Ok(()) 21 | } 22 | 23 | fn init_default_schema(self: &Arc) -> Result<(), DatabaseError> { 24 | let mut catalog = match self.catalog.try_write() { 25 | Ok(c) => c, 26 | Err(_) => { 27 | return Err(DatabaseError::CatalogError( 28 | CatalogError::CatalogLockedError, 29 | )) 30 | } 31 | }; 32 | catalog.create_schema(DEFAULT_SCHEMA.to_string()).unwrap(); 33 | Ok(()) 34 | } 35 | 36 | fn init_builtin_functions(self: &Arc) -> Result<(), DatabaseError> { 37 | let context = ClientContext::new(self.clone()); 38 | let mut buildin_funcs = BuiltinFunctions::new(context); 39 | buildin_funcs.initialize()?; 40 | Ok(()) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main_entry/errors.rs: -------------------------------------------------------------------------------- 1 | use arrow::error::ArrowError; 2 | use sqlparser::parser::ParserError; 3 | 4 | use crate::catalog_v2::CatalogError; 5 | use crate::execution::ExecutorError; 6 | use crate::function::FunctionError; 7 | use crate::planner_v2::PlannerError; 8 | 9 | #[derive(thiserror::Error, Debug)] 10 | pub enum DatabaseError { 11 | #[error("parse error: {0}")] 12 | ParserError( 13 | #[source] 14 | #[from] 15 | ParserError, 16 | ), 17 | #[error("catalog error: {0}")] 18 | CatalogError( 19 | #[source] 20 | #[from] 21 | CatalogError, 22 | ), 23 | #[error("planner error: {0}")] 24 | PlannerError( 25 | #[source] 26 | #[from] 27 | PlannerError, 28 | ), 29 | #[error("executor error: {0}")] 30 | ExecutorError( 31 | #[source] 32 | #[from] 33 | ExecutorError, 34 | ), 35 | #[error("Arrow error: {0}")] 36 | ArrowError( 37 | #[source] 38 | #[from] 39 | ArrowError, 40 | ), 41 | #[error("Function error: {0}")] 42 | FunctionError( 43 | #[source] 44 | #[from] 45 | FunctionError, 46 | ), 47 | #[error("Internal error: {0}")] 48 | InternalError(String), 49 | } 50 | -------------------------------------------------------------------------------- /src/main_entry/mod.rs: -------------------------------------------------------------------------------- 1 | mod client_context; 2 | mod db; 3 | mod errors; 4 | mod pending_query_result; 5 | mod prepared_statement_data; 6 | mod query_context; 7 | mod query_result; 8 | 9 | pub use client_context::*; 10 | pub use db::*; 11 | pub use errors::*; 12 | pub use pending_query_result::*; 13 | pub use prepared_statement_data::*; 14 | pub use query_result::*; 15 | -------------------------------------------------------------------------------- /src/main_entry/pending_query_result.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use derive_new::new; 4 | 5 | use super::{BaseQueryResult, ClientContext, DatabaseError, MaterializedQueryResult, QueryResult}; 6 | use crate::execution::ExecutionContext; 7 | 8 | #[derive(new)] 9 | pub struct PendingQueryResult { 10 | pub(crate) client_context: Arc, 11 | } 12 | 13 | impl PendingQueryResult { 14 | pub async fn execute(&self) -> Result { 15 | self.check_executable_internal().await?; 16 | 17 | let mut active_query_context = self.client_context.active_query.lock().await; 18 | let executor = active_query_context.executor.take().unwrap(); 19 | let prepared = active_query_context.prepared.take().unwrap(); 20 | // execute the query 21 | let execution_context = Arc::new(ExecutionContext::new(self.client_context.clone())); 22 | let collection = executor 23 | .try_execute(prepared.plan, execution_context) 24 | .await?; 25 | // set query result 26 | let materialized_query_result = MaterializedQueryResult::new( 27 | BaseQueryResult::new(prepared.types, prepared.names), 28 | collection, 29 | ); 30 | Ok(QueryResult::MaterializedQueryResult( 31 | materialized_query_result, 32 | )) 33 | } 34 | 35 | async fn check_executable_internal(&self) -> Result<(), DatabaseError> { 36 | // whether the current pending query is active or not 37 | let invalidated = !self.client_context.is_active_request(self).await; 38 | if invalidated { 39 | return Err(DatabaseError::InternalError( 40 | "Attempting to execute an unsuccessful or closed pending query result".to_string(), 41 | )); 42 | } 43 | Ok(()) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main_entry/prepared_statement_data.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | use sqlparser::ast::Statement; 3 | 4 | use crate::execution::PhysicalOperator; 5 | use crate::types_v2::LogicalType; 6 | 7 | #[derive(new)] 8 | #[allow(dead_code)] 9 | pub struct PreparedStatementData { 10 | /// The unbound SQL statement that was prepared 11 | pub(crate) unbound_statement: Statement, 12 | /// The fully prepared physical plan of the prepared statement 13 | pub(crate) plan: PhysicalOperator, 14 | /// The result names 15 | pub(crate) names: Vec, 16 | /// The result types 17 | pub(crate) types: Vec, 18 | } 19 | -------------------------------------------------------------------------------- /src/main_entry/query_context.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use super::{PendingQueryResult, PreparedStatementData}; 4 | use crate::execution::VolcanoExecutor; 5 | 6 | #[derive(Default)] 7 | pub struct ActiveQueryContext { 8 | /// The query that is currently being executed 9 | pub(crate) query: Option, 10 | /// The currently open result 11 | pub(crate) open_result: Option>, 12 | /// Prepared statement data 13 | pub(crate) prepared: Option, 14 | /// The query executor 15 | pub(crate) executor: Option, 16 | } 17 | 18 | impl ActiveQueryContext { 19 | pub fn reset(&mut self) { 20 | self.query = None; 21 | self.open_result = None; 22 | self.prepared = None; 23 | self.executor = None; 24 | } 25 | 26 | pub fn is_empty(&self) -> bool { 27 | self.query.is_none() 28 | && self.open_result.is_none() 29 | && self.prepared.is_none() 30 | && self.executor.is_none() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main_entry/query_result.rs: -------------------------------------------------------------------------------- 1 | use arrow::record_batch::RecordBatch; 2 | use derive_new::new; 3 | 4 | use crate::types_v2::LogicalType; 5 | 6 | #[derive(new, Debug)] 7 | pub struct BaseQueryResult { 8 | /// The SQL types of the result 9 | pub(crate) types: Vec, 10 | /// The names of the result 11 | pub(crate) names: Vec, 12 | } 13 | 14 | #[derive(new)] 15 | pub struct MaterializedQueryResult { 16 | pub(crate) base: BaseQueryResult, 17 | pub(crate) collection: Vec, 18 | } 19 | 20 | pub enum QueryResult { 21 | MaterializedQueryResult(MaterializedQueryResult), 22 | } 23 | -------------------------------------------------------------------------------- /src/optimizer/core/mod.rs: -------------------------------------------------------------------------------- 1 | mod opt_expr; 2 | mod pattern; 3 | mod rule; 4 | pub use opt_expr::*; 5 | pub use pattern::*; 6 | pub use rule::*; 7 | -------------------------------------------------------------------------------- /src/optimizer/core/opt_expr.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use crate::optimizer::{Dummy, PlanRef}; 4 | 5 | pub type OptExprNodeId = usize; 6 | 7 | #[derive(Clone)] 8 | pub enum OptExprNode { 9 | /// Raw plan node with dummy children. 10 | PlanRef(PlanRef), 11 | /// Existing OptExprNode in graph. 12 | OptExpr(OptExprNodeId), 13 | } 14 | 15 | impl Debug for OptExprNode { 16 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 17 | match self { 18 | Self::PlanRef(plan) => write!(f, "PlanRef({})", plan), 19 | Self::OptExpr(id) => write!(f, "OptExpr({})", id), 20 | } 21 | } 22 | } 23 | 24 | impl OptExprNode { 25 | pub fn get_plan_ref(&self) -> &PlanRef { 26 | match self { 27 | OptExprNode::PlanRef(plan_ref) => plan_ref, 28 | OptExprNode::OptExpr(_) => { 29 | panic!("OptExprNode::get_plan_ref() called on OptExprNode::OptExpr") 30 | } 31 | } 32 | } 33 | } 34 | 35 | /// A sub-plan-tree representation used in Rule and Matcher. Every root node could be new node or 36 | /// existing graph node. For new node, it will be added in graph, for existing node, it will be 37 | /// reconnect in graph later. 38 | /// 39 | /// It constructed by `PatternMatcher` when optimizer to match a rule, and consumed by `Rule` to do 40 | /// transformation, and `Rule` return new `OptExpr` to replace the matched sub-tree. 41 | #[derive(Clone, Debug)] 42 | pub struct OptExpr { 43 | /// The root of the tree. 44 | pub root: OptExprNode, 45 | /// The root's children expressions. 46 | pub children: Vec, 47 | } 48 | 49 | impl OptExpr { 50 | pub fn new(root: OptExprNode, children: Vec) -> Self { 51 | Self { root, children } 52 | } 53 | 54 | /// Create OptExpr tree from PlanRef tree, it will change all nodes' children to dummy nodes. 55 | pub fn new_from_plan_ref(plan: &PlanRef) -> Self { 56 | OptExpr::build_opt_expr_internal(plan) 57 | } 58 | 59 | fn build_opt_expr_internal(input: &PlanRef) -> OptExpr { 60 | // FIXME: clone with dummy children to fix comments in PatternMatcher. 61 | let root = OptExprNode::PlanRef(input.clone()); 62 | let children = input 63 | .children() 64 | .iter() 65 | .map(OptExpr::build_opt_expr_internal) 66 | .collect::>(); 67 | OptExpr { root, children } 68 | } 69 | 70 | pub fn to_plan_ref(&self) -> PlanRef { 71 | match &self.root { 72 | OptExprNode::PlanRef(p) => { 73 | let children = self 74 | .children 75 | .iter() 76 | .map(|c| c.to_plan_ref()) 77 | .collect::>(); 78 | p.clone_with_children(children) 79 | } 80 | OptExprNode::OptExpr(_) => Dummy::new_ref(), 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/optimizer/core/pattern.rs: -------------------------------------------------------------------------------- 1 | use super::OptExpr; 2 | use crate::optimizer::PlanRef; 3 | 4 | #[allow(dead_code)] 5 | pub enum PatternChildrenPredicate { 6 | /// All children and their children are matched and will be collected as 7 | /// `OptExprNode::PlanRef`. Currently used in one-time-applied rule. 8 | MatchedRecursive, 9 | /// All children will be evaluated in `PatternMatcher`, if pattern match, node will collected 10 | /// as `OptExprNode::PlanRef`. if vec is empty, it means no children are matched and 11 | /// collected. 12 | Predicate(Vec), 13 | /// We don't care the children, and them will be collected as existing nodes 14 | /// `OptExprNode::OptExpr` in OptExpr tree. 15 | None, 16 | } 17 | 18 | /// The pattern tree to match a plan tree. It defined in `Rule` and used in `PatternMatcher`. 19 | pub struct Pattern { 20 | /// The root node predicate, not contains the children. 21 | pub predicate: fn(&PlanRef) -> bool, 22 | /// The children's predicate of current node. 23 | pub children: PatternChildrenPredicate, 24 | } 25 | 26 | /// The matcher use a pattern tree to match a plan tree. 27 | /// 28 | /// Result organized in `OptExpr`. Matched nodes are `OptExprNode::PlanRef`, and non-matched 29 | /// children nodes are `OptExprNode::OptExpr`. 30 | pub trait PatternMatcher { 31 | fn match_opt_expr(&self) -> Option; 32 | } 33 | -------------------------------------------------------------------------------- /src/optimizer/core/rule.rs: -------------------------------------------------------------------------------- 1 | use enum_dispatch::enum_dispatch; 2 | 3 | use super::{OptExpr, Pattern}; 4 | 5 | /// A rule is to transform logically equivalent expression. There are two kinds of rules: 6 | /// 7 | /// - Transformation Rule: Logical to Logical 8 | /// - Implementation Rule: Logical to Physical 9 | #[enum_dispatch] 10 | pub trait Rule { 11 | /// The pattern to determine whether the rule can be applied. 12 | fn pattern(&self) -> &Pattern; 13 | 14 | /// Apply the rule and write the transformation result to `Substitute`. 15 | /// The pattern tree determines the opt_expr tree internal nodes type. 16 | fn apply(&self, opt_expr: OptExpr, result: &mut Substitute); 17 | } 18 | 19 | /// Define the transformed plans 20 | #[derive(Default)] 21 | pub struct Substitute { 22 | pub opt_exprs: Vec, 23 | } 24 | -------------------------------------------------------------------------------- /src/optimizer/expr_rewriter.rs: -------------------------------------------------------------------------------- 1 | use crate::binder::BoundExpr; 2 | 3 | pub trait ExprRewriter { 4 | fn rewrite_expr(&self, expr: &mut BoundExpr) { 5 | match expr { 6 | BoundExpr::Constant(_) => self.rewrite_constant(expr), 7 | BoundExpr::ColumnRef(_) => self.rewrite_column_ref(expr), 8 | BoundExpr::InputRef(_) => self.rewrite_input_ref(expr), 9 | BoundExpr::BinaryOp(_) => self.rewrite_binary_op(expr), 10 | BoundExpr::TypeCast(_) => self.rewrite_type_cast(expr), 11 | BoundExpr::AggFunc(_) => self.rewrite_agg_func(expr), 12 | BoundExpr::Alias(_) => self.rewrite_alias(expr), 13 | BoundExpr::Subquery(_) => self.rewrite_subquery(expr), 14 | } 15 | } 16 | 17 | fn rewrite_constant(&self, _: &mut BoundExpr) {} 18 | 19 | fn rewrite_column_ref(&self, _: &mut BoundExpr) {} 20 | 21 | fn rewrite_input_ref(&self, _: &mut BoundExpr) {} 22 | 23 | fn rewrite_type_cast(&self, _: &mut BoundExpr) {} 24 | 25 | fn rewrite_binary_op(&self, expr: &mut BoundExpr) { 26 | match expr { 27 | BoundExpr::BinaryOp(e) => { 28 | self.rewrite_expr(&mut e.left); 29 | self.rewrite_expr(&mut e.right); 30 | } 31 | _ => unreachable!(), 32 | } 33 | } 34 | 35 | fn rewrite_agg_func(&self, expr: &mut BoundExpr) { 36 | match expr { 37 | BoundExpr::AggFunc(e) => { 38 | for arg in &mut e.exprs { 39 | self.rewrite_expr(arg); 40 | } 41 | } 42 | _ => unreachable!(), 43 | } 44 | } 45 | 46 | fn rewrite_alias(&self, expr: &mut BoundExpr) { 47 | match expr { 48 | BoundExpr::Alias(e) => { 49 | self.rewrite_expr(&mut e.expr); 50 | } 51 | _ => unreachable!(), 52 | } 53 | } 54 | 55 | fn rewrite_subquery(&self, _: &mut BoundExpr) { 56 | // Do nothing due to BoundSubqueryExpr should be rewritten 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/optimizer/expr_visitor.rs: -------------------------------------------------------------------------------- 1 | use crate::binder::{ 2 | BoundAggFunc, BoundAlias, BoundBinaryOp, BoundColumnRef, BoundExpr, BoundInputRef, 3 | BoundSubqueryExpr, BoundTypeCast, 4 | }; 5 | use crate::types::ScalarValue; 6 | 7 | pub trait ExprVisitor { 8 | fn pre_visit(&mut self, _: &BoundExpr) {} 9 | 10 | fn visit_expr(&mut self, expr: &BoundExpr) { 11 | self.pre_visit(expr); 12 | match expr { 13 | BoundExpr::Constant(expr) => self.visit_constant(expr), 14 | BoundExpr::ColumnRef(expr) => self.visit_column_ref(expr), 15 | BoundExpr::InputRef(expr) => self.visit_input_ref(expr), 16 | BoundExpr::BinaryOp(expr) => self.visit_binary_op(expr), 17 | BoundExpr::TypeCast(expr) => self.visit_type_cast(expr), 18 | BoundExpr::AggFunc(expr) => self.visit_agg_func(expr), 19 | BoundExpr::Alias(expr) => self.visit_alias(expr), 20 | BoundExpr::Subquery(expr) => self.visit_subquery(expr), 21 | } 22 | } 23 | 24 | fn visit_constant(&mut self, _: &ScalarValue) {} 25 | 26 | fn visit_column_ref(&mut self, _: &BoundColumnRef) {} 27 | 28 | fn visit_input_ref(&mut self, _: &BoundInputRef) {} 29 | 30 | fn visit_binary_op(&mut self, expr: &BoundBinaryOp) { 31 | self.visit_expr(&expr.left); 32 | self.visit_expr(&expr.right); 33 | } 34 | 35 | fn visit_type_cast(&mut self, expr: &BoundTypeCast) { 36 | self.visit_expr(&expr.expr); 37 | } 38 | 39 | fn visit_agg_func(&mut self, expr: &BoundAggFunc) { 40 | for arg in &expr.exprs { 41 | self.visit_expr(arg); 42 | } 43 | } 44 | 45 | fn visit_alias(&mut self, expr: &BoundAlias) { 46 | self.visit_expr(&expr.expr); 47 | } 48 | 49 | fn visit_subquery(&mut self, _: &BoundSubqueryExpr) { 50 | // Do nothing due to BoundSubqueryExpr should be rewritten 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/optimizer/heuristic/batch.rs: -------------------------------------------------------------------------------- 1 | use crate::optimizer::RuleImpl; 2 | 3 | /// A batch of rules. 4 | #[derive(Clone)] 5 | pub struct HepBatch { 6 | pub name: String, 7 | pub strategy: HepBatchStrategy, 8 | pub rules: Vec, 9 | } 10 | 11 | impl HepBatch { 12 | pub fn new(name: String, strategy: HepBatchStrategy, rules: Vec) -> Self { 13 | Self { 14 | name, 15 | strategy, 16 | rules, 17 | } 18 | } 19 | } 20 | 21 | #[derive(Clone)] 22 | pub struct HepBatchStrategy { 23 | /// An execution strategy for rules that indicates the maximum number of executions. If the 24 | /// execution reaches fix point (i.e. converge) before maxIterations, it will stop. 25 | /// 26 | /// Fix Point means that plan tree not changed after applying all rules. 27 | pub max_iteration: usize, 28 | /// An order to traverse the plan tree nodes. 29 | pub match_order: HepMatchOrder, 30 | } 31 | 32 | impl HepBatchStrategy { 33 | pub fn once_topdown() -> Self { 34 | HepBatchStrategy { 35 | max_iteration: 1, 36 | match_order: HepMatchOrder::TopDown, 37 | } 38 | } 39 | 40 | pub fn fix_point_topdown(max_iteration: usize) -> Self { 41 | HepBatchStrategy { 42 | max_iteration, 43 | match_order: HepMatchOrder::TopDown, 44 | } 45 | } 46 | } 47 | 48 | #[derive(Clone, Copy)] 49 | pub enum HepMatchOrder { 50 | /// Match from root down. A match attempt at an ancestor always precedes all match attempts at 51 | /// its descendants. 52 | TopDown, 53 | /// Match from leaves up. A match attempt at a descendant precedes all match attempts at its 54 | /// ancestors. 55 | BottomUp, 56 | } 57 | -------------------------------------------------------------------------------- /src/optimizer/heuristic/mod.rs: -------------------------------------------------------------------------------- 1 | mod batch; 2 | mod graph; 3 | mod matcher; 4 | mod optimizer; 5 | pub use batch::*; 6 | pub use optimizer::*; 7 | -------------------------------------------------------------------------------- /src/optimizer/mod.rs: -------------------------------------------------------------------------------- 1 | mod core; 2 | mod expr_rewriter; 3 | mod expr_visitor; 4 | mod heuristic; 5 | mod input_ref_rewriter; 6 | mod physical_rewriter; 7 | mod plan_node; 8 | mod plan_rewriter; 9 | mod plan_visitor; 10 | mod rules; 11 | 12 | pub use expr_visitor::*; 13 | pub use heuristic::*; 14 | pub use input_ref_rewriter::*; 15 | pub use physical_rewriter::*; 16 | pub use plan_node::*; 17 | pub use plan_rewriter::*; 18 | pub use plan_visitor::*; 19 | pub use rules::*; 20 | -------------------------------------------------------------------------------- /src/optimizer/plan_node/dummy.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::sync::Arc; 3 | 4 | use super::{PlanNode, PlanRef, PlanTreeNode}; 5 | use crate::catalog::ColumnCatalog; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct Dummy {} 9 | 10 | impl Dummy { 11 | pub fn new_ref() -> PlanRef { 12 | Arc::new(Self {}) 13 | } 14 | 15 | pub fn new_refs(cnt: usize) -> Vec { 16 | (0..cnt).into_iter().map(|_| Dummy::new_ref()).collect() 17 | } 18 | } 19 | 20 | impl PlanNode for Dummy { 21 | fn referenced_columns(&self) -> Vec { 22 | vec![] 23 | } 24 | 25 | fn output_columns(&self) -> Vec { 26 | vec![] 27 | } 28 | } 29 | 30 | impl PlanTreeNode for Dummy { 31 | fn children(&self) -> Vec { 32 | vec![] 33 | } 34 | 35 | fn clone_with_children(&self, children: Vec) -> PlanRef { 36 | assert_eq!(children.len(), 0); 37 | Arc::new(self.clone()) 38 | } 39 | } 40 | 41 | impl fmt::Display for Dummy { 42 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 43 | writeln!(f, "Dummy:") 44 | } 45 | } 46 | 47 | impl PartialEq for Dummy { 48 | fn eq(&self, _other: &Self) -> bool { 49 | true 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/optimizer/plan_node/logical_agg.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::sync::Arc; 3 | 4 | use super::{PlanNode, PlanRef, PlanTreeNode}; 5 | use crate::binder::BoundExpr; 6 | use crate::catalog::ColumnCatalog; 7 | 8 | #[derive(Debug, Clone)] 9 | pub struct LogicalAgg { 10 | agg_funcs: Vec, 11 | group_by: Vec, 12 | input: PlanRef, 13 | } 14 | 15 | impl LogicalAgg { 16 | pub fn new(agg_funcs: Vec, group_by: Vec, input: PlanRef) -> Self { 17 | Self { 18 | agg_funcs, 19 | group_by, 20 | input, 21 | } 22 | } 23 | 24 | pub fn agg_funcs(&self) -> Vec { 25 | self.agg_funcs.clone() 26 | } 27 | 28 | pub fn group_by(&self) -> Vec { 29 | self.group_by.clone() 30 | } 31 | 32 | pub fn input(&self) -> PlanRef { 33 | self.input.clone() 34 | } 35 | } 36 | 37 | impl PlanNode for LogicalAgg { 38 | fn referenced_columns(&self) -> Vec { 39 | self.group_by 40 | .iter() 41 | .chain(self.agg_funcs.iter()) 42 | .flat_map(|e| e.get_referenced_column_catalog()) 43 | .collect::>() 44 | } 45 | 46 | fn output_columns(&self) -> Vec { 47 | self.group_by 48 | .iter() 49 | .chain(self.agg_funcs.iter()) 50 | .map(|e| e.output_column_catalog()) 51 | .collect::>() 52 | } 53 | } 54 | 55 | impl PlanTreeNode for LogicalAgg { 56 | fn children(&self) -> Vec { 57 | vec![self.input.clone()] 58 | } 59 | 60 | fn clone_with_children(&self, children: Vec) -> PlanRef { 61 | assert_eq!(children.len(), 1); 62 | Arc::new(Self::new( 63 | self.agg_funcs.clone(), 64 | self.group_by.clone(), 65 | children[0].clone(), 66 | )) 67 | } 68 | } 69 | 70 | impl fmt::Display for LogicalAgg { 71 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 72 | writeln!( 73 | f, 74 | "LogicalAgg: agg_funcs {:?} group_by {:?}", 75 | self.agg_funcs(), 76 | self.group_by(), 77 | ) 78 | } 79 | } 80 | 81 | impl PartialEq for LogicalAgg { 82 | fn eq(&self, other: &Self) -> bool { 83 | self.agg_funcs == other.agg_funcs 84 | && self.group_by == other.group_by 85 | && self.input == other.input() 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/optimizer/plan_node/logical_filter.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::sync::Arc; 3 | 4 | use super::{PlanNode, PlanRef, PlanTreeNode}; 5 | use crate::binder::BoundExpr; 6 | use crate::catalog::ColumnCatalog; 7 | 8 | #[derive(Debug, Clone)] 9 | pub struct LogicalFilter { 10 | /// filtered expression on input PlanRef 11 | expr: BoundExpr, 12 | /// the child PlanRef to be projected 13 | input: PlanRef, 14 | } 15 | 16 | impl LogicalFilter { 17 | pub fn new(expr: BoundExpr, input: PlanRef) -> Self { 18 | Self { expr, input } 19 | } 20 | 21 | pub fn expr(&self) -> BoundExpr { 22 | self.expr.clone() 23 | } 24 | 25 | pub fn input(&self) -> PlanRef { 26 | self.input.clone() 27 | } 28 | } 29 | 30 | impl PlanNode for LogicalFilter { 31 | fn referenced_columns(&self) -> Vec { 32 | self.expr.get_referenced_column_catalog() 33 | } 34 | 35 | fn output_columns(&self) -> Vec { 36 | self.children()[0].output_columns() 37 | } 38 | } 39 | 40 | impl PlanTreeNode for LogicalFilter { 41 | fn children(&self) -> Vec { 42 | vec![self.input.clone()] 43 | } 44 | 45 | fn clone_with_children(&self, children: Vec) -> PlanRef { 46 | assert_eq!(children.len(), 1); 47 | Arc::new(Self::new(self.expr.clone(), children[0].clone())) 48 | } 49 | } 50 | 51 | impl fmt::Display for LogicalFilter { 52 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 53 | writeln!(f, "LogicalFilter: expr {:?}", self.expr) 54 | } 55 | } 56 | 57 | impl PartialEq for LogicalFilter { 58 | fn eq(&self, other: &Self) -> bool { 59 | self.expr == other.expr && self.input == other.input() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/optimizer/plan_node/logical_limit.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::sync::Arc; 3 | 4 | use super::{PlanNode, PlanRef, PlanTreeNode}; 5 | use crate::binder::BoundExpr; 6 | use crate::catalog::ColumnCatalog; 7 | 8 | #[derive(Debug, Clone)] 9 | pub struct LogicalLimit { 10 | limit: Option, 11 | offset: Option, 12 | input: PlanRef, 13 | } 14 | 15 | impl LogicalLimit { 16 | pub fn new(limit: Option, offset: Option, input: PlanRef) -> Self { 17 | Self { 18 | limit, 19 | offset, 20 | input, 21 | } 22 | } 23 | 24 | pub fn limit(&self) -> Option { 25 | self.limit.clone() 26 | } 27 | 28 | pub fn offset(&self) -> Option { 29 | self.offset.clone() 30 | } 31 | 32 | pub fn input(&self) -> PlanRef { 33 | self.input.clone() 34 | } 35 | } 36 | 37 | impl PlanNode for LogicalLimit { 38 | fn referenced_columns(&self) -> Vec { 39 | vec![] 40 | } 41 | 42 | fn output_columns(&self) -> Vec { 43 | self.children()[0].output_columns() 44 | } 45 | } 46 | 47 | impl PlanTreeNode for LogicalLimit { 48 | fn children(&self) -> Vec { 49 | vec![self.input.clone()] 50 | } 51 | 52 | fn clone_with_children(&self, children: Vec) -> PlanRef { 53 | assert_eq!(children.len(), 1); 54 | Arc::new(Self::new(self.limit(), self.offset(), children[0].clone())) 55 | } 56 | } 57 | 58 | impl fmt::Display for LogicalLimit { 59 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 60 | writeln!( 61 | f, 62 | "LogicalLimit: limit {:?}, offset {:?}", 63 | self.limit, self.offset 64 | ) 65 | } 66 | } 67 | 68 | impl PartialEq for LogicalLimit { 69 | fn eq(&self, other: &Self) -> bool { 70 | self.limit == other.limit && self.offset == other.offset && self.input == other.input() 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/optimizer/plan_node/logical_order.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::sync::Arc; 3 | 4 | use super::{PlanNode, PlanRef, PlanTreeNode}; 5 | use crate::binder::BoundOrderBy; 6 | use crate::catalog::ColumnCatalog; 7 | 8 | #[derive(Debug, Clone)] 9 | pub struct LogicalOrder { 10 | order_by: Vec, 11 | input: PlanRef, 12 | } 13 | 14 | impl LogicalOrder { 15 | pub fn new(order_by: Vec, input: PlanRef) -> Self { 16 | Self { order_by, input } 17 | } 18 | 19 | pub fn order_by(&self) -> Vec { 20 | self.order_by.clone() 21 | } 22 | 23 | pub fn input(&self) -> PlanRef { 24 | self.input.clone() 25 | } 26 | } 27 | 28 | impl PlanNode for LogicalOrder { 29 | fn referenced_columns(&self) -> Vec { 30 | self.order_by 31 | .iter() 32 | .flat_map(|e| e.expr.get_referenced_column_catalog()) 33 | .collect::>() 34 | } 35 | 36 | fn output_columns(&self) -> Vec { 37 | self.children()[0].output_columns() 38 | } 39 | } 40 | 41 | impl PlanTreeNode for LogicalOrder { 42 | fn children(&self) -> Vec { 43 | vec![self.input.clone()] 44 | } 45 | 46 | fn clone_with_children(&self, children: Vec) -> PlanRef { 47 | assert_eq!(children.len(), 1); 48 | Arc::new(Self::new(self.order_by(), children[0].clone())) 49 | } 50 | } 51 | 52 | impl fmt::Display for LogicalOrder { 53 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 54 | writeln!(f, "LogicalOrder: order {:?}", self.order_by) 55 | } 56 | } 57 | 58 | impl PartialEq for LogicalOrder { 59 | fn eq(&self, other: &Self) -> bool { 60 | self.order_by == other.order_by && self.input == other.input() 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/optimizer/plan_node/logical_project.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::sync::Arc; 3 | 4 | use super::{PlanNode, PlanRef, PlanTreeNode}; 5 | use crate::binder::BoundExpr; 6 | use crate::catalog::ColumnCatalog; 7 | 8 | #[derive(Debug, Clone)] 9 | pub struct LogicalProject { 10 | /// evaluated projection expressions on input PlanRef 11 | exprs: Vec, 12 | /// the child PlanRef to be projected 13 | input: PlanRef, 14 | } 15 | 16 | impl LogicalProject { 17 | pub fn new(exprs: Vec, input: PlanRef) -> Self { 18 | Self { exprs, input } 19 | } 20 | 21 | pub fn exprs(&self) -> Vec { 22 | self.exprs.clone() 23 | } 24 | 25 | pub fn input(&self) -> PlanRef { 26 | self.input.clone() 27 | } 28 | } 29 | 30 | impl PlanNode for LogicalProject { 31 | fn referenced_columns(&self) -> Vec { 32 | self.exprs 33 | .iter() 34 | .flat_map(|e| e.get_referenced_column_catalog()) 35 | .collect::>() 36 | } 37 | 38 | fn output_columns(&self) -> Vec { 39 | self.exprs 40 | .iter() 41 | .map(|e| e.output_column_catalog()) 42 | .collect::>() 43 | } 44 | } 45 | 46 | impl PlanTreeNode for LogicalProject { 47 | fn children(&self) -> Vec { 48 | vec![self.input.clone()] 49 | } 50 | 51 | fn clone_with_children(&self, children: Vec) -> PlanRef { 52 | assert_eq!(children.len(), 1); 53 | Arc::new(Self::new(self.exprs.clone(), children[0].clone())) 54 | } 55 | } 56 | 57 | impl fmt::Display for LogicalProject { 58 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 59 | writeln!(f, "LogicalProject: exprs {:?}", self.exprs) 60 | } 61 | } 62 | 63 | impl PartialEq for LogicalProject { 64 | fn eq(&self, other: &Self) -> bool { 65 | self.exprs == other.exprs && self.input == other.input() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/optimizer/plan_node/physical_cross_join.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use std::sync::Arc; 3 | 4 | use super::{LogicalJoin, PlanNode, PlanRef, PlanTreeNode}; 5 | use crate::binder::JoinType; 6 | use crate::catalog::ColumnCatalog; 7 | 8 | #[derive(Debug, Clone)] 9 | pub struct PhysicalCrossJoin { 10 | logical: LogicalJoin, 11 | } 12 | 13 | impl PhysicalCrossJoin { 14 | pub fn new(logical: LogicalJoin) -> Self { 15 | Self { logical } 16 | } 17 | 18 | pub fn left(&self) -> PlanRef { 19 | self.logical.left() 20 | } 21 | 22 | pub fn right(&self) -> PlanRef { 23 | self.logical.right() 24 | } 25 | 26 | pub fn join_type(&self) -> JoinType { 27 | self.logical.join_type() 28 | } 29 | 30 | pub fn logical(&self) -> &LogicalJoin { 31 | &self.logical 32 | } 33 | 34 | pub fn join_output_columns(&self) -> Vec { 35 | self.logical.join_output_columns() 36 | } 37 | } 38 | 39 | impl PlanNode for PhysicalCrossJoin { 40 | fn referenced_columns(&self) -> Vec { 41 | self.logical.referenced_columns() 42 | } 43 | 44 | fn output_columns(&self) -> Vec { 45 | self.logical().output_columns() 46 | } 47 | } 48 | 49 | impl PlanTreeNode for PhysicalCrossJoin { 50 | fn children(&self) -> Vec { 51 | vec![self.left(), self.right()] 52 | } 53 | 54 | fn clone_with_children(&self, children: Vec) -> PlanRef { 55 | let p = self.logical().clone_with_children(children); 56 | Arc::new(Self::new(p.as_logical_join().unwrap().clone())) 57 | } 58 | } 59 | 60 | impl fmt::Display for PhysicalCrossJoin { 61 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 62 | writeln!(f, "PhysicalCrossJoin: type {:?}", self.join_type(),) 63 | } 64 | } 65 | 66 | impl PartialEq for PhysicalCrossJoin { 67 | fn eq(&self, other: &Self) -> bool { 68 | self.join_type() == other.join_type() 69 | && self.left() == other.left() 70 | && self.right() == other.right() 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/optimizer/plan_node/physical_filter.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use std::sync::Arc; 3 | 4 | use super::{LogicalFilter, PlanNode, PlanRef, PlanTreeNode}; 5 | use crate::catalog::ColumnCatalog; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct PhysicalFilter { 9 | logical: LogicalFilter, 10 | } 11 | 12 | impl PhysicalFilter { 13 | pub fn new(logical: LogicalFilter) -> Self { 14 | Self { logical } 15 | } 16 | 17 | pub fn logical(&self) -> &LogicalFilter { 18 | &self.logical 19 | } 20 | } 21 | 22 | impl PlanNode for PhysicalFilter { 23 | fn referenced_columns(&self) -> Vec { 24 | self.logical.referenced_columns() 25 | } 26 | 27 | fn output_columns(&self) -> Vec { 28 | self.logical().output_columns() 29 | } 30 | } 31 | 32 | impl PlanTreeNode for PhysicalFilter { 33 | fn children(&self) -> Vec { 34 | self.logical().children() 35 | } 36 | 37 | fn clone_with_children(&self, children: Vec) -> PlanRef { 38 | let p = self.logical().clone_with_children(children); 39 | Arc::new(Self::new(p.as_logical_filter().unwrap().clone())) 40 | } 41 | } 42 | 43 | impl fmt::Display for PhysicalFilter { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 | writeln!(f, "PhysicalFilter: expr {:?}", self.logical().expr()) 46 | } 47 | } 48 | 49 | impl PartialEq for PhysicalFilter { 50 | fn eq(&self, other: &Self) -> bool { 51 | self.logical == other.logical 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/optimizer/plan_node/physical_hash_agg.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::sync::Arc; 3 | 4 | use super::{LogicalAgg, PlanNode, PlanRef, PlanTreeNode}; 5 | use crate::catalog::ColumnCatalog; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct PhysicalHashAgg { 9 | logical: LogicalAgg, 10 | } 11 | 12 | impl PhysicalHashAgg { 13 | pub fn new(logical: LogicalAgg) -> Self { 14 | Self { logical } 15 | } 16 | 17 | pub fn logical(&self) -> &LogicalAgg { 18 | &self.logical 19 | } 20 | } 21 | 22 | impl PlanNode for PhysicalHashAgg { 23 | fn referenced_columns(&self) -> Vec { 24 | self.logical.referenced_columns() 25 | } 26 | 27 | fn output_columns(&self) -> Vec { 28 | self.logical().output_columns() 29 | } 30 | } 31 | 32 | impl PlanTreeNode for PhysicalHashAgg { 33 | fn children(&self) -> Vec { 34 | self.logical().children() 35 | } 36 | 37 | fn clone_with_children(&self, children: Vec) -> PlanRef { 38 | let p = self.logical().clone_with_children(children); 39 | Arc::new(Self::new(p.as_logical_agg().unwrap().clone())) 40 | } 41 | } 42 | 43 | impl fmt::Display for PhysicalHashAgg { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 | writeln!( 46 | f, 47 | "PhysicalHashAgg: agg_funcs {:?} group_by {:?}", 48 | self.logical().agg_funcs(), 49 | self.logical().group_by(), 50 | ) 51 | } 52 | } 53 | 54 | impl PartialEq for PhysicalHashAgg { 55 | fn eq(&self, other: &Self) -> bool { 56 | self.logical == other.logical 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/optimizer/plan_node/physical_hash_join.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use std::sync::Arc; 3 | 4 | use super::{LogicalJoin, PlanNode, PlanRef, PlanTreeNode}; 5 | use crate::binder::{JoinCondition, JoinType}; 6 | use crate::catalog::ColumnCatalog; 7 | 8 | #[derive(Debug, Clone)] 9 | pub struct PhysicalHashJoin { 10 | logical: LogicalJoin, 11 | } 12 | 13 | impl PhysicalHashJoin { 14 | pub fn new(logical: LogicalJoin) -> Self { 15 | Self { logical } 16 | } 17 | 18 | pub fn left(&self) -> PlanRef { 19 | self.logical.left() 20 | } 21 | 22 | pub fn right(&self) -> PlanRef { 23 | self.logical.right() 24 | } 25 | 26 | pub fn join_type(&self) -> JoinType { 27 | self.logical.join_type() 28 | } 29 | 30 | pub fn join_condition(&self) -> JoinCondition { 31 | self.logical.join_condition() 32 | } 33 | 34 | pub fn logical(&self) -> &LogicalJoin { 35 | &self.logical 36 | } 37 | 38 | pub fn join_output_columns(&self) -> Vec { 39 | self.logical.join_output_columns() 40 | } 41 | } 42 | 43 | impl PlanNode for PhysicalHashJoin { 44 | fn referenced_columns(&self) -> Vec { 45 | self.logical.referenced_columns() 46 | } 47 | 48 | fn output_columns(&self) -> Vec { 49 | self.logical().output_columns() 50 | } 51 | } 52 | 53 | impl PlanTreeNode for PhysicalHashJoin { 54 | fn children(&self) -> Vec { 55 | vec![self.left(), self.right()] 56 | } 57 | 58 | fn clone_with_children(&self, children: Vec) -> PlanRef { 59 | let p = self.logical().clone_with_children(children); 60 | Arc::new(Self::new(p.as_logical_join().unwrap().clone())) 61 | } 62 | } 63 | 64 | impl fmt::Display for PhysicalHashJoin { 65 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 66 | writeln!( 67 | f, 68 | "PhysicalHashJoin: type {:?}, cond {:?}", 69 | self.join_type(), 70 | self.join_condition() 71 | ) 72 | } 73 | } 74 | 75 | impl PartialEq for PhysicalHashJoin { 76 | fn eq(&self, other: &Self) -> bool { 77 | self.join_type() == other.join_type() 78 | && self.join_condition() == other.join_condition() 79 | && self.left() == other.left() 80 | && self.right() == other.right() 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/optimizer/plan_node/physical_limit.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use std::sync::Arc; 3 | 4 | use super::{LogicalLimit, PlanNode, PlanRef, PlanTreeNode}; 5 | use crate::catalog::ColumnCatalog; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct PhysicalLimit { 9 | logical: LogicalLimit, 10 | } 11 | 12 | impl PhysicalLimit { 13 | pub fn new(logical: LogicalLimit) -> Self { 14 | Self { logical } 15 | } 16 | 17 | pub fn logical(&self) -> &LogicalLimit { 18 | &self.logical 19 | } 20 | } 21 | 22 | impl PlanNode for PhysicalLimit { 23 | fn referenced_columns(&self) -> Vec { 24 | self.logical.referenced_columns() 25 | } 26 | 27 | fn output_columns(&self) -> Vec { 28 | self.logical().output_columns() 29 | } 30 | } 31 | 32 | impl PlanTreeNode for PhysicalLimit { 33 | fn children(&self) -> Vec { 34 | self.logical().children() 35 | } 36 | 37 | fn clone_with_children(&self, children: Vec) -> PlanRef { 38 | let p = self.logical().clone_with_children(children); 39 | Arc::new(Self::new(p.as_logical_limit().unwrap().clone())) 40 | } 41 | } 42 | 43 | impl fmt::Display for PhysicalLimit { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 | writeln!( 46 | f, 47 | "PhysicalLimit: limit {:?}, offset {:?}", 48 | self.logical().limit(), 49 | self.logical().offset(), 50 | ) 51 | } 52 | } 53 | 54 | impl PartialEq for PhysicalLimit { 55 | fn eq(&self, other: &Self) -> bool { 56 | self.logical == other.logical 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/optimizer/plan_node/physical_order.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use std::sync::Arc; 3 | 4 | use super::{LogicalOrder, PlanNode, PlanRef, PlanTreeNode}; 5 | use crate::catalog::ColumnCatalog; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct PhysicalOrder { 9 | logical: LogicalOrder, 10 | } 11 | 12 | impl PhysicalOrder { 13 | pub fn new(logical: LogicalOrder) -> Self { 14 | Self { logical } 15 | } 16 | 17 | pub fn logical(&self) -> &LogicalOrder { 18 | &self.logical 19 | } 20 | } 21 | 22 | impl PlanNode for PhysicalOrder { 23 | fn referenced_columns(&self) -> Vec { 24 | self.logical.referenced_columns() 25 | } 26 | 27 | fn output_columns(&self) -> Vec { 28 | self.logical().output_columns() 29 | } 30 | } 31 | 32 | impl PlanTreeNode for PhysicalOrder { 33 | fn children(&self) -> Vec { 34 | self.logical().children() 35 | } 36 | 37 | fn clone_with_children(&self, children: Vec) -> PlanRef { 38 | let p = self.logical().clone_with_children(children); 39 | Arc::new(Self::new(p.as_logical_order().unwrap().clone())) 40 | } 41 | } 42 | 43 | impl fmt::Display for PhysicalOrder { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 | writeln!(f, "PhysicalOrder: Order {:?}", self.logical().order_by()) 46 | } 47 | } 48 | 49 | impl PartialEq for PhysicalOrder { 50 | fn eq(&self, other: &Self) -> bool { 51 | self.logical == other.logical 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/optimizer/plan_node/physical_project.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::sync::Arc; 3 | 4 | use super::{LogicalProject, PlanNode, PlanRef, PlanTreeNode}; 5 | use crate::catalog::ColumnCatalog; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct PhysicalProject { 9 | logical: LogicalProject, 10 | } 11 | 12 | impl PhysicalProject { 13 | pub fn new(logical: LogicalProject) -> Self { 14 | Self { logical } 15 | } 16 | 17 | pub fn logical(&self) -> &LogicalProject { 18 | &self.logical 19 | } 20 | } 21 | 22 | impl PlanNode for PhysicalProject { 23 | fn referenced_columns(&self) -> Vec { 24 | self.logical.referenced_columns() 25 | } 26 | 27 | fn output_columns(&self) -> Vec { 28 | self.logical().output_columns() 29 | } 30 | } 31 | 32 | impl PlanTreeNode for PhysicalProject { 33 | fn children(&self) -> Vec { 34 | self.logical().children() 35 | } 36 | 37 | fn clone_with_children(&self, children: Vec) -> PlanRef { 38 | let p = self.logical().clone_with_children(children); 39 | Arc::new(Self::new(p.as_logical_project().unwrap().clone())) 40 | } 41 | } 42 | 43 | impl fmt::Display for PhysicalProject { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 | writeln!(f, "PhysicalProject: exprs {:?}", self.logical().exprs()) 46 | } 47 | } 48 | 49 | impl PartialEq for PhysicalProject { 50 | fn eq(&self, other: &Self) -> bool { 51 | self.logical == other.logical 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/optimizer/plan_node/physical_simple_agg.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::sync::Arc; 3 | 4 | use super::{LogicalAgg, PlanNode, PlanRef, PlanTreeNode}; 5 | use crate::catalog::ColumnCatalog; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct PhysicalSimpleAgg { 9 | logical: LogicalAgg, 10 | } 11 | 12 | impl PhysicalSimpleAgg { 13 | pub fn new(logical: LogicalAgg) -> Self { 14 | Self { logical } 15 | } 16 | 17 | pub fn logical(&self) -> &LogicalAgg { 18 | &self.logical 19 | } 20 | } 21 | 22 | impl PlanNode for PhysicalSimpleAgg { 23 | fn referenced_columns(&self) -> Vec { 24 | self.logical.referenced_columns() 25 | } 26 | 27 | fn output_columns(&self) -> Vec { 28 | self.logical().output_columns() 29 | } 30 | } 31 | 32 | impl PlanTreeNode for PhysicalSimpleAgg { 33 | fn children(&self) -> Vec { 34 | self.logical().children() 35 | } 36 | 37 | fn clone_with_children(&self, children: Vec) -> PlanRef { 38 | let p = self.logical().clone_with_children(children); 39 | Arc::new(Self::new(p.as_logical_agg().unwrap().clone())) 40 | } 41 | } 42 | 43 | impl fmt::Display for PhysicalSimpleAgg { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 | writeln!( 46 | f, 47 | "PhysicalSimpleAgg: agg_funcs {:?} group_by {:?}", 48 | self.logical().agg_funcs(), 49 | self.logical().group_by(), 50 | ) 51 | } 52 | } 53 | 54 | impl PartialEq for PhysicalSimpleAgg { 55 | fn eq(&self, other: &Self) -> bool { 56 | self.logical == other.logical 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/optimizer/plan_node/physical_table_scan.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::sync::Arc; 3 | 4 | use super::{LogicalTableScan, PlanNode, PlanRef, PlanTreeNode}; 5 | use crate::catalog::ColumnCatalog; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct PhysicalTableScan { 9 | logical: LogicalTableScan, 10 | } 11 | 12 | impl PhysicalTableScan { 13 | pub fn new(logical: LogicalTableScan) -> Self { 14 | Self { logical } 15 | } 16 | 17 | pub fn logical(&self) -> &LogicalTableScan { 18 | &self.logical 19 | } 20 | } 21 | 22 | impl PlanNode for PhysicalTableScan { 23 | fn referenced_columns(&self) -> Vec { 24 | self.logical.referenced_columns() 25 | } 26 | 27 | fn output_columns(&self) -> Vec { 28 | self.logical().output_columns() 29 | } 30 | } 31 | 32 | impl PlanTreeNode for PhysicalTableScan { 33 | fn children(&self) -> Vec { 34 | vec![] 35 | } 36 | 37 | fn clone_with_children(&self, children: Vec) -> PlanRef { 38 | assert_eq!(children.len(), 0); 39 | Arc::new(self.clone()) 40 | } 41 | } 42 | 43 | impl fmt::Display for PhysicalTableScan { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 | let bounds_str = self 46 | .logical() 47 | .bounds() 48 | .map(|b| format!(", bounds: (offset:{},limit:{})", b.0, b.1)) 49 | .unwrap_or_else(|| "".into()); 50 | let alias = self 51 | .logical() 52 | .table_alias() 53 | .map(|alias| format!(" as {}", alias)) 54 | .unwrap_or_else(|| "".into()); 55 | writeln!( 56 | f, 57 | "PhysicalTableScan: table: #{}{}, columns: [{}]{}", 58 | self.logical().table_id(), 59 | alias, 60 | self.logical().column_ids().join(", "), 61 | bounds_str, 62 | ) 63 | } 64 | } 65 | 66 | impl PartialEq for PhysicalTableScan { 67 | fn eq(&self, other: &Self) -> bool { 68 | self.logical == other.logical 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/optimizer/plan_rewriter.rs: -------------------------------------------------------------------------------- 1 | use itertools::Itertools; 2 | use paste::paste; 3 | 4 | use super::plan_node::*; 5 | use crate::for_all_plan_nodes; 6 | 7 | macro_rules! def_rewriter { 8 | ($($node_name:ident),*) => { 9 | pub trait PlanRewriter { 10 | paste! { 11 | fn rewrite(&mut self, plan: PlanRef) -> PlanRef { 12 | match plan.node_type() { 13 | $( 14 | PlanNodeType::$node_name => self.[](plan.downcast_ref::<$node_name>().unwrap()), 15 | )* 16 | } 17 | } 18 | 19 | $( 20 | fn [](&mut self, plan: &$node_name) -> PlanRef { 21 | let new_children = plan 22 | .children() 23 | .into_iter() 24 | .map(|child| self.rewrite(child.clone())) 25 | .collect_vec(); 26 | plan.clone_with_children(new_children) 27 | } 28 | )* 29 | } 30 | } 31 | }; 32 | } 33 | 34 | for_all_plan_nodes! { def_rewriter } 35 | -------------------------------------------------------------------------------- /src/optimizer/plan_visitor.rs: -------------------------------------------------------------------------------- 1 | use paste::paste; 2 | 3 | use super::plan_node::*; 4 | use crate::for_all_plan_nodes; 5 | 6 | macro_rules! def_rewriter { 7 | ($($node_name:ident),*) => { 8 | pub trait PlanVisitor { 9 | paste! { 10 | fn visit(&mut self, plan: PlanRef) -> Option { 11 | match plan.node_type() { 12 | $( 13 | PlanNodeType::$node_name => self.[](plan.downcast_ref::<$node_name>().unwrap()), 14 | )* 15 | } 16 | } 17 | 18 | $( 19 | fn [](&mut self, _plan: &$node_name) -> Option { 20 | unimplemented!("The {} is not implemented visitor yet", stringify!($node_name)) 21 | } 22 | )* 23 | } 24 | } 25 | }; 26 | } 27 | 28 | for_all_plan_nodes! { def_rewriter } 29 | -------------------------------------------------------------------------------- /src/optimizer/rules/physical_rewrite.rs: -------------------------------------------------------------------------------- 1 | use super::RuleImpl; 2 | use crate::optimizer::core::*; 3 | use crate::optimizer::{PhysicalRewriter, PlanRewriter}; 4 | 5 | lazy_static! { 6 | static ref PATTERN: Pattern = { 7 | Pattern { 8 | predicate: |p| p.is_logical_plan(), 9 | children: PatternChildrenPredicate::MatchedRecursive, 10 | } 11 | }; 12 | } 13 | 14 | #[derive(Clone)] 15 | pub struct PhysicalRewriteRule; 16 | 17 | impl PhysicalRewriteRule { 18 | pub fn create() -> RuleImpl { 19 | Self {}.into() 20 | } 21 | } 22 | 23 | impl Rule for PhysicalRewriteRule { 24 | fn pattern(&self) -> &Pattern { 25 | &PATTERN 26 | } 27 | 28 | fn apply(&self, opt_expr: OptExpr, result: &mut Substitute) { 29 | let mut rewriter = PhysicalRewriter::default(); 30 | let plan = opt_expr.to_plan_ref(); 31 | let new_plan = rewriter.rewrite(plan); 32 | 33 | let res = OptExpr::new_from_plan_ref(&new_plan); 34 | result.opt_exprs.push(res); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/parser/mod.rs: -------------------------------------------------------------------------------- 1 | use sqlparser::ast::{Query, Statement}; 2 | use sqlparser::dialect::PostgreSqlDialect; 3 | use sqlparser::parser::{Parser, ParserError}; 4 | 5 | pub fn parse(sql: &str) -> Result, ParserError> { 6 | let dialect = PostgreSqlDialect {}; 7 | let stmts = Parser::parse_sql(&dialect, sql)?; 8 | if stmts.is_empty() { 9 | return Err(ParserError::ParserError("empty string".to_string())); 10 | } 11 | Ok(stmts) 12 | } 13 | 14 | pub struct Sqlparser {} 15 | 16 | impl Sqlparser { 17 | pub fn parse(sql: String) -> Result, ParserError> { 18 | let dialect = PostgreSqlDialect {}; 19 | let stmts = Parser::parse_sql(&dialect, sql.as_str())?; 20 | Ok(stmts) 21 | } 22 | 23 | pub fn parse_one_stmt(sql: &str) -> Result { 24 | let dialect = PostgreSqlDialect {}; 25 | let stmts = Parser::parse_sql(&dialect, sql)?; 26 | if stmts.len() != 1 { 27 | return Err(ParserError::ParserError( 28 | "not a single statement".to_string(), 29 | )); 30 | } 31 | Ok(stmts[0].clone()) 32 | } 33 | 34 | pub fn parse_one_query(sql: &str) -> Result, ParserError> { 35 | let dialect = PostgreSqlDialect {}; 36 | let stmts = Parser::parse_sql(&dialect, sql)?; 37 | if stmts.len() != 1 { 38 | return Err(ParserError::ParserError( 39 | "not a single statement".to_string(), 40 | )); 41 | } 42 | match stmts[0].clone() { 43 | Statement::Query(q) => Ok(q), 44 | _ => Err(ParserError::ParserError( 45 | "only expect query statement".to_string(), 46 | )), 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/planner/util.rs: -------------------------------------------------------------------------------- 1 | // The following idea inspired from datafusion expr part. 2 | 3 | use crate::binder::BoundExpr; 4 | use crate::optimizer::ExprVisitor; 5 | 6 | // Visitor that find expressions that match a particular predicate 7 | struct ExprFinder<'a, F> 8 | where 9 | F: Fn(&BoundExpr) -> bool, 10 | { 11 | test_fn: &'a F, 12 | exprs: Vec, 13 | } 14 | 15 | impl<'a, F> ExprFinder<'a, F> 16 | where 17 | F: Fn(&BoundExpr) -> bool, 18 | { 19 | fn new(test_fn: &'a F) -> Self { 20 | Self { 21 | test_fn, 22 | exprs: Vec::new(), 23 | } 24 | } 25 | } 26 | 27 | impl<'a, F> ExprVisitor for ExprFinder<'a, F> 28 | where 29 | F: Fn(&BoundExpr) -> bool, 30 | { 31 | fn pre_visit(&mut self, expr: &BoundExpr) { 32 | if (self.test_fn)(expr) && !self.exprs.contains(expr) { 33 | self.exprs.push(expr.clone()); 34 | } 35 | } 36 | } 37 | 38 | /// Search an `Expr`, and all of its nested `Expr`'s, for any that pass the 39 | /// provided test. The returned `Expr`'s are deduplicated and returned in order 40 | /// of appearance (depth first). 41 | fn find_exprs_in_expr(expr: &BoundExpr, test_fn: &F) -> Vec 42 | where 43 | F: Fn(&BoundExpr) -> bool, 44 | { 45 | let mut finder = ExprFinder::new(test_fn); 46 | finder.visit_expr(expr); 47 | finder.exprs 48 | } 49 | 50 | fn find_exprs_in_exprs(exprs: &[BoundExpr], test_fn: &F) -> Vec 51 | where 52 | F: Fn(&BoundExpr) -> bool, 53 | { 54 | exprs 55 | .iter() 56 | .flat_map(|expr| find_exprs_in_expr(expr, test_fn)) 57 | .fold(vec![], |mut acc, expr| { 58 | if !acc.contains(&expr) { 59 | acc.push(expr) 60 | } 61 | acc 62 | }) 63 | } 64 | 65 | /// Collect all deeply nested `Expr::AggregateFunction` and 66 | /// `Expr::AggregateUDF`. They are returned in order of occurrence (depth 67 | /// first), with duplicates omitted. 68 | pub fn find_aggregate_exprs(exprs: &[BoundExpr]) -> Vec { 69 | find_exprs_in_exprs(exprs, &|nested_expr| { 70 | matches!(nested_expr, BoundExpr::AggFunc { .. }) 71 | }) 72 | } 73 | -------------------------------------------------------------------------------- /src/planner_v2/binder/binding.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use derive_new::new; 4 | 5 | use super::{BindError, BoundColumnRefExpression, BoundExpressionBase, ColumnBinding}; 6 | use crate::catalog_v2::CatalogEntry; 7 | use crate::types_v2::LogicalType; 8 | 9 | /// A Binding represents a binding to a table, table-producing function 10 | /// or subquery with a specified table index. 11 | #[derive(new, Clone, Debug)] 12 | pub struct Binding { 13 | /// The alias of the binding 14 | pub(crate) alias: String, 15 | /// The table index of the binding 16 | pub(crate) index: usize, 17 | pub(crate) types: Vec, 18 | #[allow(dead_code)] 19 | pub(crate) names: Vec, 20 | /// Name -> index for the names 21 | pub(crate) name_map: HashMap, 22 | /// The underlying catalog entry (if any) 23 | #[new(default)] 24 | pub(crate) catalog_entry: Option, 25 | } 26 | impl Binding { 27 | pub fn has_match_binding(&self, column_name: &str) -> bool { 28 | self.try_get_binding_index(column_name).is_some() 29 | } 30 | 31 | pub fn try_get_binding_index(&self, column_name: &str) -> Option { 32 | self.name_map.get(column_name).cloned() 33 | } 34 | 35 | pub fn bind_column( 36 | &self, 37 | column_name: &str, 38 | depth: usize, 39 | ) -> Result { 40 | if let Some(col_idx) = self.try_get_binding_index(column_name) { 41 | let col_type = self.types[col_idx].clone(); 42 | let col_binding = ColumnBinding::new(self.index, col_idx); 43 | Ok(BoundColumnRefExpression::new( 44 | BoundExpressionBase::new(column_name.to_string(), col_type), 45 | col_binding, 46 | depth, 47 | )) 48 | } else { 49 | Err(BindError::Internal(format!( 50 | "Column {} not found in table {}", 51 | column_name, self.alias 52 | ))) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/planner_v2/binder/errors.rs: -------------------------------------------------------------------------------- 1 | use sqlparser::parser::ParserError; 2 | 3 | use crate::catalog_v2::CatalogError; 4 | use crate::execution::ExecutorError; 5 | use crate::function::FunctionError; 6 | 7 | #[derive(thiserror::Error, Debug)] 8 | pub enum BindError { 9 | #[error("unsupported expr: {0}")] 10 | UnsupportedExpr(String), 11 | #[error("unsupported statement: {0}")] 12 | UnsupportedStmt(String), 13 | #[error("sqlparser unsupported statement: {0}")] 14 | SqlParserUnsupportedStmt(String), 15 | #[error("bind internal error: {0}")] 16 | Internal(String), 17 | #[error("{0}")] 18 | FunctionBindError(String), 19 | #[error("type error: {0}")] 20 | TypeError( 21 | #[from] 22 | #[source] 23 | crate::types_v2::TypeError, 24 | ), 25 | #[error("catalog error: {0}")] 26 | CatalogError( 27 | #[from] 28 | #[source] 29 | CatalogError, 30 | ), 31 | #[error("function error: {0}")] 32 | FunctionError( 33 | #[from] 34 | #[source] 35 | FunctionError, 36 | ), 37 | #[error("executor error: {0}")] 38 | ExecutorError( 39 | #[from] 40 | #[source] 41 | ExecutorError, 42 | ), 43 | #[error("parse error: {0}")] 44 | ParserError( 45 | #[from] 46 | #[source] 47 | ParserError, 48 | ), 49 | } 50 | -------------------------------------------------------------------------------- /src/planner_v2/binder/expression/bind_cast_expression.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::{BoundExpression, BoundExpressionBase}; 4 | use crate::function::{CastFunction, DefaultCastFunctions}; 5 | use crate::planner_v2::BindError; 6 | use crate::types_v2::LogicalType; 7 | 8 | #[derive(new, Debug, Clone)] 9 | pub struct BoundCastExpression { 10 | pub(crate) base: BoundExpressionBase, 11 | /// The child type 12 | pub(crate) child: Box, 13 | /// Whether to use try_cast or not. try_cast converts cast failures into NULLs instead of 14 | /// throwing an error. 15 | pub(crate) try_cast: bool, 16 | /// The cast function to execute 17 | pub(crate) function: CastFunction, 18 | } 19 | 20 | impl BoundCastExpression { 21 | /// If source_expr return_type is same type as target_type, return source_expr directly, 22 | /// otherwise, add a cast expression to the source_expr. 23 | pub fn try_add_cast_to_type( 24 | source_expr: BoundExpression, 25 | target_type: LogicalType, 26 | try_cast: bool, 27 | ) -> Result { 28 | let source_type = source_expr.return_type(); 29 | if source_type == target_type { 30 | return Ok(source_expr); 31 | } 32 | let cast_function = DefaultCastFunctions::get_cast_function(&source_type, &target_type)?; 33 | let alias = format!("cast({}) as {}", source_expr.alias(), target_type); 34 | let base = BoundExpressionBase::new(alias, target_type); 35 | Ok(BoundExpression::BoundCastExpression( 36 | BoundCastExpression::new(base, Box::new(source_expr), try_cast, cast_function), 37 | )) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/planner_v2/binder/expression/bind_comparison_expression.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::{BoundCastExpression, BoundExpression, BoundExpressionBase}; 4 | use crate::function::{ComparisonFunction, DefaultComparisonFunctions}; 5 | use crate::planner_v2::{BindError, ExpressionBinder}; 6 | use crate::types_v2::LogicalType; 7 | 8 | #[derive(new, Debug, Clone)] 9 | pub struct BoundComparisonExpression { 10 | pub(crate) base: BoundExpressionBase, 11 | pub(crate) left: Box, 12 | pub(crate) right: Box, 13 | /// The comparison function to execute 14 | pub(crate) function: ComparisonFunction, 15 | } 16 | 17 | impl ExpressionBinder<'_> { 18 | pub fn bind_comparison_expression( 19 | &mut self, 20 | left: &sqlparser::ast::Expr, 21 | op: &sqlparser::ast::BinaryOperator, 22 | right: &sqlparser::ast::Expr, 23 | result_names: &mut Vec, 24 | result_types: &mut Vec, 25 | ) -> Result { 26 | let mut bound_left = self.bind_expression(left, &mut vec![], &mut vec![])?; 27 | let mut bound_right = self.bind_expression(right, &mut vec![], &mut vec![])?; 28 | let left_type = bound_left.return_type(); 29 | let right_type = bound_right.return_type(); 30 | 31 | // cast the input types to the same type, now obtain the result type of the input types 32 | let input_type = LogicalType::max_logical_type(&left_type, &right_type)?; 33 | bound_left = 34 | BoundCastExpression::try_add_cast_to_type(bound_left, input_type.clone(), true)?; 35 | bound_right = 36 | BoundCastExpression::try_add_cast_to_type(bound_right, input_type.clone(), true)?; 37 | 38 | result_names.push(format!( 39 | "{}({},{})", 40 | op, 41 | bound_left.alias(), 42 | bound_right.alias() 43 | )); 44 | result_types.push(LogicalType::Boolean); 45 | let function = DefaultComparisonFunctions::get_comparison_function(op, &input_type)?; 46 | let base = BoundExpressionBase::new("".to_string(), LogicalType::Boolean); 47 | Ok(BoundExpression::BoundComparisonExpression( 48 | BoundComparisonExpression::new( 49 | base, 50 | Box::new(bound_left), 51 | Box::new(bound_right), 52 | function, 53 | ), 54 | )) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/planner_v2/binder/expression/bind_conjunction_expression.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::{BoundCastExpression, BoundExpression, BoundExpressionBase}; 4 | use crate::function::{ConjunctionFunction, DefaultConjunctionFunctions}; 5 | use crate::planner_v2::{BindError, ExpressionBinder}; 6 | use crate::types_v2::LogicalType; 7 | 8 | #[derive(new, Debug, Clone)] 9 | pub struct BoundConjunctionExpression { 10 | pub(crate) base: BoundExpressionBase, 11 | pub(crate) function: ConjunctionFunction, 12 | pub(crate) children: Vec, 13 | } 14 | 15 | impl BoundConjunctionExpression { 16 | /// If expressions count larger than 1, build a and conjunction expression, otherwise return the 17 | /// first expression 18 | pub fn try_build_and_conjunction_expression( 19 | expressions: Vec, 20 | ) -> BoundExpression { 21 | assert!(!expressions.is_empty()); 22 | // conjuct expression with and make only one expression 23 | if expressions.len() > 1 { 24 | let base = BoundExpressionBase::new("".to_string(), LogicalType::Boolean); 25 | let and_func = DefaultConjunctionFunctions::get_conjunction_function( 26 | &sqlparser::ast::BinaryOperator::And, 27 | ) 28 | .unwrap(); 29 | BoundExpression::BoundConjunctionExpression(BoundConjunctionExpression::new( 30 | base, 31 | and_func, 32 | expressions, 33 | )) 34 | } else { 35 | expressions[0].clone() 36 | } 37 | } 38 | } 39 | 40 | impl ExpressionBinder<'_> { 41 | pub fn bind_conjunction_expression( 42 | &mut self, 43 | left: &sqlparser::ast::Expr, 44 | op: &sqlparser::ast::BinaryOperator, 45 | right: &sqlparser::ast::Expr, 46 | result_names: &mut Vec, 47 | result_types: &mut Vec, 48 | ) -> Result { 49 | let function = DefaultConjunctionFunctions::get_conjunction_function(op)?; 50 | 51 | let mut return_names = vec![]; 52 | let mut left = self.bind_expression(left, &mut return_names, &mut vec![])?; 53 | left = BoundCastExpression::try_add_cast_to_type(left, LogicalType::Boolean, true)?; 54 | return_names[0] = left.alias(); 55 | let mut right = self.bind_expression(right, &mut return_names, &mut vec![])?; 56 | right = BoundCastExpression::try_add_cast_to_type(right, LogicalType::Boolean, true)?; 57 | return_names[1] = right.alias(); 58 | 59 | result_names.push(format!("{}({},{})", op, return_names[0], return_names[1])); 60 | result_types.push(LogicalType::Boolean); 61 | let base = BoundExpressionBase::new("".to_string(), LogicalType::Boolean); 62 | Ok(BoundExpression::BoundConjunctionExpression( 63 | BoundConjunctionExpression::new(base, function, vec![left, right]), 64 | )) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/planner_v2/binder/expression/bind_constant_expression.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::{BoundExpression, BoundExpressionBase}; 4 | use crate::planner_v2::{BindError, ExpressionBinder}; 5 | use crate::types_v2::{LogicalType, ScalarValue}; 6 | 7 | #[derive(new, Debug, Clone)] 8 | pub struct BoundConstantExpression { 9 | pub(crate) base: BoundExpressionBase, 10 | pub(crate) value: ScalarValue, 11 | } 12 | 13 | impl ExpressionBinder<'_> { 14 | pub fn bind_constant_expr( 15 | &self, 16 | v: &sqlparser::ast::Value, 17 | result_names: &mut Vec, 18 | result_types: &mut Vec, 19 | ) -> Result { 20 | let scalar: ScalarValue = v.into(); 21 | let base = BoundExpressionBase::new(scalar.to_string(), scalar.get_logical_type()); 22 | result_names.push(base.alias.clone()); 23 | result_types.push(base.return_type.clone()); 24 | let expr = 25 | BoundExpression::BoundConstantExpression(BoundConstantExpression::new(base, scalar)); 26 | Ok(expr) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/planner_v2/binder/expression/bind_function_expression.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::{BoundExpression, BoundExpressionBase}; 4 | use crate::catalog_v2::{Catalog, DEFAULT_SCHEMA}; 5 | use crate::function::ScalarFunction; 6 | use crate::planner_v2::{BindError, ExpressionBinder, FunctionBinder}; 7 | use crate::types_v2::LogicalType; 8 | 9 | #[derive(new, Debug, Clone)] 10 | pub struct BoundFunctionExpression { 11 | pub(crate) base: BoundExpressionBase, 12 | /// The bound function expression 13 | pub(crate) function: ScalarFunction, 14 | /// List of child-expressions of the function 15 | pub(crate) children: Vec, 16 | } 17 | 18 | impl ExpressionBinder<'_> { 19 | pub fn bind_function_expression( 20 | &mut self, 21 | left: &sqlparser::ast::Expr, 22 | op: &sqlparser::ast::BinaryOperator, 23 | right: &sqlparser::ast::Expr, 24 | result_names: &mut Vec, 25 | result_types: &mut Vec, 26 | ) -> Result { 27 | let function_name = match op { 28 | sqlparser::ast::BinaryOperator::Plus => "add", 29 | sqlparser::ast::BinaryOperator::Minus => "subtract", 30 | sqlparser::ast::BinaryOperator::Multiply => "multiply", 31 | sqlparser::ast::BinaryOperator::Divide => "divide", 32 | other => { 33 | return Err(BindError::Internal(format!( 34 | "unexpected binary operator {} for function expression", 35 | other 36 | ))) 37 | } 38 | }; 39 | let function = Catalog::get_scalar_function( 40 | self.binder.clone_client_context(), 41 | DEFAULT_SCHEMA.to_string(), 42 | function_name.to_string(), 43 | )?; 44 | let mut return_names = vec![]; 45 | let left = self.bind_expression(left, &mut return_names, &mut vec![])?; 46 | let right = self.bind_expression(right, &mut return_names, &mut vec![])?; 47 | let func_binder = FunctionBinder::new(); 48 | let bound_function = func_binder.bind_scalar_function(function, vec![left, right])?; 49 | result_names.push(format!("{}({})", function_name, return_names.join(", "))); 50 | result_types.push(bound_function.base.return_type.clone()); 51 | Ok(BoundExpression::BoundFunctionExpression(bound_function)) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/planner_v2/binder/expression/bind_reference_expression.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::BoundExpressionBase; 4 | 5 | /// A BoundReferenceExpression represents a physical index into a DataChunk 6 | #[derive(new, Debug, Clone)] 7 | pub struct BoundReferenceExpression { 8 | pub(crate) base: BoundExpressionBase, 9 | /// Index used to access data in the chunks 10 | pub(crate) index: usize, 11 | } 12 | -------------------------------------------------------------------------------- /src/planner_v2/binder/expression/column_binding.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | #[derive(new, Clone, Debug, PartialEq, Eq, Hash)] 4 | pub struct ColumnBinding { 5 | pub(crate) table_idx: usize, 6 | pub(crate) column_idx: usize, 7 | } 8 | -------------------------------------------------------------------------------- /src/planner_v2/binder/expression_binder/column_alias_binder.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use derive_new::new; 4 | 5 | #[derive(new)] 6 | pub struct ColumnAliasData { 7 | pub(crate) original_select_items: Vec, 8 | pub(crate) alias_map: HashMap, 9 | } 10 | -------------------------------------------------------------------------------- /src/planner_v2/binder/expression_binder/mod.rs: -------------------------------------------------------------------------------- 1 | mod column_alias_binder; 2 | pub use column_alias_binder::*; 3 | -------------------------------------------------------------------------------- /src/planner_v2/binder/mod.rs: -------------------------------------------------------------------------------- 1 | mod bind_context; 2 | mod binding; 3 | mod errors; 4 | mod expression; 5 | mod expression_binder; 6 | mod query_node; 7 | mod sqlparser_util; 8 | mod statement; 9 | mod tableref; 10 | 11 | use std::sync::Arc; 12 | 13 | pub use bind_context::*; 14 | pub use binding::*; 15 | pub use errors::*; 16 | pub use expression::*; 17 | pub use expression_binder::*; 18 | pub use query_node::*; 19 | pub use sqlparser_util::*; 20 | pub use statement::*; 21 | pub use tableref::*; 22 | 23 | use crate::main_entry::ClientContext; 24 | 25 | #[derive(Clone)] 26 | pub struct Binder { 27 | client_context: Arc, 28 | bind_context: BindContext, 29 | /// The count of bound_tables 30 | bound_tables: usize, 31 | #[allow(dead_code)] 32 | parent: Option>, 33 | } 34 | 35 | impl Binder { 36 | pub fn new(client_context: Arc) -> Self { 37 | Self { 38 | client_context, 39 | bind_context: BindContext::new(), 40 | bound_tables: 0, 41 | parent: None, 42 | } 43 | } 44 | 45 | pub fn new_with_parent(client_context: Arc, parent: Arc) -> Self { 46 | Self { 47 | client_context, 48 | bind_context: BindContext::new(), 49 | bound_tables: 0, 50 | parent: Some(parent), 51 | } 52 | } 53 | 54 | pub fn clone_client_context(&self) -> Arc { 55 | self.client_context.clone() 56 | } 57 | 58 | pub fn generate_table_index(&mut self) -> usize { 59 | self.bound_tables += 1; 60 | self.bound_tables 61 | } 62 | 63 | pub fn has_match_binding(&mut self, table_name: &str, column_name: &str) -> bool { 64 | let binding = self.bind_context.get_binding(table_name); 65 | if binding.is_none() { 66 | return false; 67 | } 68 | let binding = binding.unwrap(); 69 | binding.has_match_binding(column_name) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/planner_v2/binder/query_node/bind_result_modifier.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | use sqlparser::ast::{Expr, Query}; 3 | 4 | use crate::execution::ExpressionExecutor; 5 | use crate::planner_v2::{ 6 | BindError, Binder, BoundCastExpression, BoundExpression, ExpressionBinder, 7 | }; 8 | use crate::types_v2::{LogicalType, ScalarValue}; 9 | 10 | #[derive(Debug)] 11 | pub enum BoundResultModifier { 12 | BoundLimitModifier(BoundLimitModifier), 13 | } 14 | 15 | #[derive(new, Debug)] 16 | pub struct BoundLimitModifier { 17 | pub(crate) limit_value: u64, 18 | pub(crate) offsert_value: u64, 19 | pub(crate) limit: Option, 20 | pub(crate) offset: Option, 21 | } 22 | 23 | impl Binder { 24 | fn bind_delimiter( 25 | expr_binder: &mut ExpressionBinder, 26 | expr: &Expr, 27 | ) -> Result { 28 | let bound_expr = expr_binder.bind_expression(expr, &mut vec![], &mut vec![])?; 29 | let new_expr = 30 | BoundCastExpression::try_add_cast_to_type(bound_expr, LogicalType::UBigint, false)?; 31 | Ok(new_expr) 32 | } 33 | 34 | fn cast_delimiter_val(val: ScalarValue) -> u64 { 35 | match val { 36 | ScalarValue::UInt64(Some(v)) => v, 37 | _ => unreachable!("delimiter val must be int64 due to previous cast"), 38 | } 39 | } 40 | 41 | pub fn bind_limit_modifier( 42 | &mut self, 43 | query: &Query, 44 | ) -> Result, BindError> { 45 | let mut expr_binder = ExpressionBinder::new(self); 46 | let limit = query 47 | .limit 48 | .as_ref() 49 | .map(|expr| Self::bind_delimiter(&mut expr_binder, expr)) 50 | .transpose()?; 51 | let limit_value = if let Some(limit_expr) = &limit { 52 | let val = ExpressionExecutor::execute_scalar(limit_expr)?; 53 | Self::cast_delimiter_val(val) 54 | } else { 55 | u64::max_value() 56 | }; 57 | 58 | let offset = query 59 | .offset 60 | .as_ref() 61 | .map(|expr| Self::bind_delimiter(&mut expr_binder, &expr.value)) 62 | .transpose()?; 63 | let offsert_value = if let Some(offset_expr) = &offset { 64 | let val = ExpressionExecutor::execute_scalar(offset_expr)?; 65 | Self::cast_delimiter_val(val) 66 | } else { 67 | 0 68 | }; 69 | 70 | let modifier = if limit.is_none() && offset.is_none() { 71 | None 72 | } else { 73 | Some(BoundResultModifier::BoundLimitModifier( 74 | BoundLimitModifier::new(limit_value, offsert_value, limit, offset), 75 | )) 76 | }; 77 | 78 | Ok(modifier) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/planner_v2/binder/query_node/mod.rs: -------------------------------------------------------------------------------- 1 | mod bind_result_modifier; 2 | mod bind_select_node; 3 | mod plan_result_modifier; 4 | mod plan_select_node; 5 | pub use bind_result_modifier::*; 6 | pub use bind_select_node::*; 7 | pub use plan_result_modifier::*; 8 | pub use plan_select_node::*; 9 | -------------------------------------------------------------------------------- /src/planner_v2/binder/query_node/plan_result_modifier.rs: -------------------------------------------------------------------------------- 1 | use super::BoundResultModifier; 2 | use crate::planner_v2::{BindError, Binder, LogicalLimit, LogicalOperator, LogicalOperatorBase}; 3 | 4 | impl Binder { 5 | pub fn plan_for_result_modifiers( 6 | &mut self, 7 | result_modifiers: Vec, 8 | root: LogicalOperator, 9 | ) -> Result { 10 | let mut root_op = root; 11 | for modifier in result_modifiers.into_iter() { 12 | match modifier { 13 | BoundResultModifier::BoundLimitModifier(limit) => { 14 | let mut op = LogicalOperator::LogicalLimit(LogicalLimit::new( 15 | LogicalOperatorBase::default(), 16 | limit.limit_value, 17 | limit.offsert_value, 18 | limit.limit, 19 | limit.offset, 20 | )); 21 | op.add_child(root_op); 22 | root_op = op; 23 | } 24 | } 25 | } 26 | Ok(root_op) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/planner_v2/binder/statement/bind_explain.rs: -------------------------------------------------------------------------------- 1 | use sqlparser::ast::Statement; 2 | 3 | use super::BoundStatement; 4 | use crate::planner_v2::{ 5 | BindError, Binder, ExplainType, LogicalExplain, LogicalOperator, LogicalOperatorBase, 6 | }; 7 | use crate::types_v2::LogicalType; 8 | use crate::util::tree_render::TreeRender; 9 | 10 | impl Binder { 11 | pub fn bind_explain(&mut self, stmt: &Statement) -> Result { 12 | match stmt { 13 | Statement::Explain { 14 | statement, analyze, .. 15 | } => { 16 | let bound_stmt = self.bind(statement)?; 17 | let explain_type = if *analyze { 18 | ExplainType::ANALYZE 19 | } else { 20 | ExplainType::STANDARD 21 | }; 22 | 23 | let types = vec![LogicalType::Varchar, LogicalType::Varchar]; 24 | let names = vec!["explain_type".to_string(), "explain_value".to_string()]; 25 | let logical_plan_string = TreeRender::logical_plan_tree(&bound_stmt.plan); 26 | let base = LogicalOperatorBase::new(vec![bound_stmt.plan], vec![], vec![]); 27 | let logical_explain = LogicalExplain::new(base, explain_type, logical_plan_string); 28 | let new_plan = LogicalOperator::LogicalExplain(logical_explain); 29 | Ok(BoundStatement::new(new_plan, types, names)) 30 | } 31 | _ => Err(BindError::UnsupportedStmt(format!("{:?}", stmt))), 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/planner_v2/binder/statement/bind_explain_table.rs: -------------------------------------------------------------------------------- 1 | use sqlparser::ast::Statement; 2 | 3 | use super::BoundStatement; 4 | use crate::planner_v2::{ 5 | BindError, Binder, SqlparserQueryBuilder, SqlparserResolver, SqlparserSelectBuilder, 6 | }; 7 | 8 | impl Binder { 9 | pub fn bind_explain_table(&mut self, stmt: &Statement) -> Result { 10 | match stmt { 11 | Statement::ExplainTable { 12 | describe_alias, 13 | table_name, 14 | .. 15 | } => { 16 | if !*describe_alias { 17 | return Err(BindError::UnsupportedStmt( 18 | "Only support describe table statement".to_string(), 19 | )); 20 | } 21 | let (_, table_name) = SqlparserResolver::object_name_to_schema_table(table_name)?; 22 | let select = SqlparserSelectBuilder::default() 23 | .projection_wildcard() 24 | .from_table_function("sqlrs_columns") 25 | .selection_col_eq_string("table_name", table_name.as_str()) 26 | .build(); 27 | let query = SqlparserQueryBuilder::new_from_select(select).build(); 28 | self.bind_query(&query) 29 | } 30 | _ => Err(BindError::UnsupportedStmt(format!("{:?}", stmt))), 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/planner_v2/binder/statement/bind_select.rs: -------------------------------------------------------------------------------- 1 | use sqlparser::ast::{Query, Statement}; 2 | 3 | use super::BoundStatement; 4 | use crate::planner_v2::{BindError, Binder}; 5 | 6 | impl Binder { 7 | pub fn bind_query_stmt(&mut self, stmt: &Statement) -> Result { 8 | match stmt { 9 | Statement::Query(query) => self.bind_query(query), 10 | _ => Err(BindError::UnsupportedStmt(format!("{:?}", stmt))), 11 | } 12 | } 13 | 14 | pub fn bind_query(&mut self, query: &Query) -> Result { 15 | let mut node = self.bind_query_body(&query.body)?; 16 | if let Some(limit_modifier) = self.bind_limit_modifier(query)? { 17 | node.modifiers.push(limit_modifier); 18 | } 19 | self.create_plan_for_select_node(node) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/planner_v2/binder/statement/bind_show_tables.rs: -------------------------------------------------------------------------------- 1 | use sqlparser::ast::Statement; 2 | 3 | use super::BoundStatement; 4 | use crate::planner_v2::{BindError, Binder, SqlparserQueryBuilder, SqlparserSelectBuilder}; 5 | 6 | impl Binder { 7 | pub fn bind_show_tables(&mut self, stmt: &Statement) -> Result { 8 | match stmt { 9 | Statement::ShowTables { .. } => { 10 | let select = SqlparserSelectBuilder::default() 11 | .projection_cols(vec!["schema_name", "table_name"]) 12 | .from_table_function("sqlrs_tables") 13 | .build(); 14 | let query = SqlparserQueryBuilder::new_from_select(select).build(); 15 | self.bind_query(&query) 16 | } 17 | _ => Err(BindError::UnsupportedStmt(format!("{:?}", stmt))), 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/planner_v2/binder/statement/mod.rs: -------------------------------------------------------------------------------- 1 | mod bind_copy; 2 | mod bind_create; 3 | mod bind_explain; 4 | mod bind_explain_table; 5 | mod bind_insert; 6 | mod bind_select; 7 | mod bind_show_tables; 8 | 9 | pub use bind_create::*; 10 | use derive_new::new; 11 | use sqlparser::ast::Statement; 12 | 13 | use super::{BindError, Binder}; 14 | use crate::planner_v2::LogicalOperator; 15 | use crate::types_v2::LogicalType; 16 | 17 | #[derive(new, Debug)] 18 | pub struct BoundStatement { 19 | pub(crate) plan: LogicalOperator, 20 | pub(crate) types: Vec, 21 | pub(crate) names: Vec, 22 | } 23 | 24 | impl Binder { 25 | pub fn bind(&mut self, statement: &Statement) -> Result { 26 | match statement { 27 | Statement::CreateTable { .. } => self.bind_create_table(statement), 28 | Statement::Insert { .. } => self.bind_insert(statement), 29 | Statement::Query { .. } => self.bind_query_stmt(statement), 30 | Statement::Explain { .. } => self.bind_explain(statement), 31 | Statement::ShowTables { .. } => self.bind_show_tables(statement), 32 | Statement::ExplainTable { .. } => self.bind_explain_table(statement), 33 | Statement::Copy { .. } => self.bind_copy(statement), 34 | _ => Err(BindError::UnsupportedStmt(format!("{:?}", statement))), 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/planner_v2/binder/tableref/bind_dummy_table_ref.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::BoundTableRef; 4 | use crate::planner_v2::{BindError, Binder}; 5 | 6 | #[derive(new, Debug)] 7 | pub struct BoundDummyTableRef { 8 | pub(crate) bind_index: usize, 9 | } 10 | 11 | impl Binder { 12 | pub fn bind_dummy_table_ref(&mut self) -> Result { 13 | let table_index = self.generate_table_index(); 14 | let bound_tabel_ref = 15 | BoundTableRef::BoundDummyTableRef(BoundDummyTableRef::new(table_index)); 16 | Ok(bound_tabel_ref) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/planner_v2/binder/tableref/mod.rs: -------------------------------------------------------------------------------- 1 | mod bind_base_table_ref; 2 | mod bind_dummy_table_ref; 3 | mod bind_expression_list_ref; 4 | mod bind_table_function; 5 | mod plan_base_table_ref; 6 | mod plan_dummy_table_ref; 7 | mod plan_expression_list_ref; 8 | mod plan_table_function; 9 | 10 | pub use bind_base_table_ref::*; 11 | pub use bind_dummy_table_ref::*; 12 | pub use bind_expression_list_ref::*; 13 | pub use bind_table_function::*; 14 | pub use plan_base_table_ref::*; 15 | pub use plan_dummy_table_ref::*; 16 | pub use plan_expression_list_ref::*; 17 | pub use plan_table_function::*; 18 | 19 | use super::{BindError, Binder}; 20 | 21 | #[derive(Debug)] 22 | pub enum BoundTableRef { 23 | BoundExpressionListRef(BoundExpressionListRef), 24 | BoundBaseTableRef(Box), 25 | BoundDummyTableRef(BoundDummyTableRef), 26 | BoundTableFunction(Box), 27 | } 28 | 29 | impl Binder { 30 | pub fn bind_table_ref( 31 | &mut self, 32 | table_refs: &[sqlparser::ast::TableWithJoins], 33 | ) -> Result { 34 | if table_refs.is_empty() { 35 | return self.bind_dummy_table_ref(); 36 | } 37 | let first_table = table_refs[0].clone(); 38 | match first_table.relation.clone() { 39 | sqlparser::ast::TableFactor::Table { .. } => { 40 | self.bind_base_table_ref(first_table.relation) 41 | } 42 | other => Err(BindError::Internal(format!( 43 | "unexpected table type: {}", 44 | other 45 | ))), 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/planner_v2/binder/tableref/plan_base_table_ref.rs: -------------------------------------------------------------------------------- 1 | use super::BoundBaseTableRef; 2 | use crate::planner_v2::{BindError, Binder, LogicalOperator}; 3 | 4 | impl Binder { 5 | pub fn create_plan_for_base_tabel_ref( 6 | &mut self, 7 | bound_ref: BoundBaseTableRef, 8 | ) -> Result { 9 | Ok(bound_ref.get) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/planner_v2/binder/tableref/plan_dummy_table_ref.rs: -------------------------------------------------------------------------------- 1 | use super::BoundDummyTableRef; 2 | use crate::planner_v2::{BindError, Binder, LogicalDummyScan, LogicalOperator}; 3 | 4 | impl Binder { 5 | pub fn create_plan_for_dummy_table_ref( 6 | &mut self, 7 | bound_ref: BoundDummyTableRef, 8 | ) -> Result { 9 | Ok(LogicalOperator::LogicalDummyScan(LogicalDummyScan::new( 10 | bound_ref.bind_index, 11 | ))) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/planner_v2/binder/tableref/plan_expression_list_ref.rs: -------------------------------------------------------------------------------- 1 | use super::BoundExpressionListRef; 2 | use crate::planner_v2::{ 3 | BindError, Binder, LogicalDummyScan, LogicalExpressionGet, LogicalOperator, LogicalOperatorBase, 4 | }; 5 | 6 | impl Binder { 7 | pub fn create_plan_for_expression_list_ref( 8 | &mut self, 9 | bound_ref: BoundExpressionListRef, 10 | ) -> Result { 11 | let table_idx = bound_ref.bind_index; 12 | let base = LogicalOperatorBase::new( 13 | vec![LogicalOperator::LogicalDummyScan(LogicalDummyScan::new( 14 | self.generate_table_index(), 15 | ))], 16 | vec![], 17 | vec![], 18 | ); 19 | let plan = LogicalExpressionGet::new(base, table_idx, bound_ref.types, bound_ref.values); 20 | Ok(LogicalOperator::LogicalExpressionGet(plan)) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/planner_v2/binder/tableref/plan_table_function.rs: -------------------------------------------------------------------------------- 1 | use super::BoundTableFunction; 2 | use crate::planner_v2::{BindError, Binder, LogicalOperator}; 3 | 4 | impl Binder { 5 | pub fn create_plan_for_table_function( 6 | &mut self, 7 | bound_ref: BoundTableFunction, 8 | ) -> Result { 9 | Ok(bound_ref.get) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/planner_v2/constants.rs: -------------------------------------------------------------------------------- 1 | pub static INVALID_INDEX: usize = std::usize::MAX; 2 | -------------------------------------------------------------------------------- /src/planner_v2/errors.rs: -------------------------------------------------------------------------------- 1 | use super::BindError; 2 | 3 | #[derive(thiserror::Error, Debug)] 4 | pub enum PlannerError { 5 | #[error("bind error: {0}")] 6 | BindError( 7 | #[from] 8 | #[source] 9 | BindError, 10 | ), 11 | } 12 | -------------------------------------------------------------------------------- /src/planner_v2/expression_iterator.rs: -------------------------------------------------------------------------------- 1 | use super::BoundExpression; 2 | 3 | pub struct ExpressionIterator; 4 | 5 | impl ExpressionIterator { 6 | pub fn enumerate_children(expr: &mut BoundExpression, callback: F) 7 | where 8 | F: Fn(&mut BoundExpression), 9 | { 10 | match expr { 11 | BoundExpression::BoundColumnRefExpression(_) 12 | | BoundExpression::BoundConstantExpression(_) 13 | | BoundExpression::BoundReferenceExpression(_) => { 14 | // these node types have no children 15 | } 16 | BoundExpression::BoundCastExpression(e) => callback(&mut e.child), 17 | BoundExpression::BoundFunctionExpression(e) => e.children.iter_mut().for_each(callback), 18 | BoundExpression::BoundComparisonExpression(e) => { 19 | callback(&mut e.left); 20 | callback(&mut e.right); 21 | } 22 | BoundExpression::BoundConjunctionExpression(e) => { 23 | e.children.iter_mut().for_each(callback) 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/planner_v2/mod.rs: -------------------------------------------------------------------------------- 1 | mod binder; 2 | mod constants; 3 | mod errors; 4 | mod expression_binder; 5 | mod expression_iterator; 6 | mod function_binder; 7 | mod logical_operator_visitor; 8 | mod operator; 9 | 10 | use std::sync::Arc; 11 | 12 | pub use binder::*; 13 | pub use constants::*; 14 | pub use errors::*; 15 | pub use expression_binder::*; 16 | pub use expression_iterator::*; 17 | pub use function_binder::*; 18 | use log::debug; 19 | pub use logical_operator_visitor::*; 20 | pub use operator::*; 21 | use sqlparser::ast::Statement; 22 | 23 | use crate::main_entry::ClientContext; 24 | use crate::types_v2::LogicalType; 25 | use crate::util::tree_render::TreeRender; 26 | 27 | static LOGGING_TARGET: &str = "sqlrs::planner"; 28 | 29 | pub struct Planner { 30 | binder: Binder, 31 | #[allow(dead_code)] 32 | client_context: Arc, 33 | pub(crate) plan: Option, 34 | pub(crate) types: Option>, 35 | pub(crate) names: Option>, 36 | } 37 | 38 | impl Planner { 39 | pub fn new(client_context: Arc) -> Self { 40 | Self { 41 | binder: Binder::new(client_context.clone()), 42 | client_context, 43 | plan: None, 44 | types: None, 45 | names: None, 46 | } 47 | } 48 | 49 | pub fn create_plan(&mut self, statement: &Statement) -> Result<(), PlannerError> { 50 | debug!( 51 | target: LOGGING_TARGET, 52 | "Planner raw statement: {:?}", statement 53 | ); 54 | 55 | let bound_statement = self.binder.bind(statement)?; 56 | 57 | debug!( 58 | target: LOGGING_TARGET, 59 | r#"Planner bound_statement: 60 | names: {:?} 61 | types: {:?} 62 | plan: 63 | {}"#, 64 | bound_statement.names, 65 | bound_statement.types, 66 | TreeRender::logical_plan_tree(&bound_statement.plan), 67 | ); 68 | self.plan = Some(bound_statement.plan); 69 | self.names = Some(bound_statement.names); 70 | self.types = Some(bound_statement.types); 71 | Ok(()) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/planner_v2/operator/logical_create_table.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::LogicalOperatorBase; 4 | use crate::planner_v2::BoundCreateTableInfo; 5 | 6 | #[derive(new, Debug, Clone)] 7 | pub struct LogicalCreateTable { 8 | #[new(default)] 9 | pub(crate) base: LogicalOperatorBase, 10 | pub(crate) info: BoundCreateTableInfo, 11 | } 12 | -------------------------------------------------------------------------------- /src/planner_v2/operator/logical_dummy_scan.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::LogicalOperatorBase; 4 | 5 | /// LogicalDummyScan represents a dummy scan returning a single row. 6 | #[derive(new, Debug, Clone)] 7 | pub struct LogicalDummyScan { 8 | #[new(default)] 9 | pub(crate) base: LogicalOperatorBase, 10 | pub(crate) table_idx: usize, 11 | } 12 | -------------------------------------------------------------------------------- /src/planner_v2/operator/logical_explain.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::LogicalOperatorBase; 4 | 5 | #[derive(new, Debug, Clone)] 6 | pub struct LogicalExplain { 7 | pub(crate) base: LogicalOperatorBase, 8 | #[allow(dead_code)] 9 | pub(crate) explain_type: ExplainType, 10 | /// un-optimized logical plan explain string 11 | pub(crate) logical_plan: String, 12 | } 13 | 14 | #[derive(Debug, Clone)] 15 | pub enum ExplainType { 16 | STANDARD, 17 | ANALYZE, 18 | } 19 | -------------------------------------------------------------------------------- /src/planner_v2/operator/logical_expression_get.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::LogicalOperatorBase; 4 | use crate::planner_v2::BoundExpression; 5 | use crate::types_v2::LogicalType; 6 | 7 | /// LogicalExpressionGet represents a scan operation over a set of to-be-executed expressions 8 | #[derive(new, Debug, Clone)] 9 | pub struct LogicalExpressionGet { 10 | pub(crate) base: LogicalOperatorBase, 11 | pub(crate) table_idx: usize, 12 | /// The types of the expressions 13 | pub(crate) expr_types: Vec, 14 | /// The set of expressions 15 | pub(crate) expressions: Vec>, 16 | } 17 | -------------------------------------------------------------------------------- /src/planner_v2/operator/logical_get.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::LogicalOperatorBase; 4 | use crate::function::{FunctionData, TableFunction}; 5 | use crate::types_v2::LogicalType; 6 | 7 | /// LogicalGet represents a scan operation from a data source 8 | #[derive(new, Debug, Clone)] 9 | pub struct LogicalGet { 10 | pub(crate) base: LogicalOperatorBase, 11 | pub(crate) table_idx: usize, 12 | /// The function that is called 13 | pub(crate) function: TableFunction, 14 | // The bind data of the function 15 | pub(crate) bind_data: Option, 16 | /// The types of ALL columns that can be returned by the table function 17 | pub(crate) returned_types: Vec, 18 | /// The names of ALL columns that can be returned by the table function 19 | pub(crate) names: Vec, 20 | /// Bound column IDs 21 | #[new(default)] 22 | #[allow(dead_code)] 23 | pub(crate) column_ids: Vec, 24 | } 25 | -------------------------------------------------------------------------------- /src/planner_v2/operator/logical_insert.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::LogicalOperatorBase; 4 | use crate::catalog_v2::TableCatalogEntry; 5 | use crate::types_v2::LogicalType; 6 | 7 | #[derive(new, Debug, Clone)] 8 | pub struct LogicalInsert { 9 | pub(crate) base: LogicalOperatorBase, 10 | /// The insertion map ([table_index -> index in result, or INVALID_INDEX if not specified]) 11 | pub(crate) column_index_list: Vec, 12 | /// The expected types for the INSERT statement 13 | pub(crate) expected_types: Vec, 14 | pub(crate) table: TableCatalogEntry, 15 | } 16 | -------------------------------------------------------------------------------- /src/planner_v2/operator/logical_limit.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::LogicalOperatorBase; 4 | use crate::planner_v2::BoundExpression; 5 | 6 | #[derive(new, Debug, Clone)] 7 | pub struct LogicalLimit { 8 | pub(crate) base: LogicalOperatorBase, 9 | pub(crate) limit_value: u64, 10 | pub(crate) offsert_value: u64, 11 | pub(crate) limit: Option, 12 | pub(crate) offset: Option, 13 | } 14 | -------------------------------------------------------------------------------- /src/planner_v2/operator/logical_projection.rs: -------------------------------------------------------------------------------- 1 | use derive_new::new; 2 | 3 | use super::LogicalOperatorBase; 4 | 5 | #[derive(new, Debug, Clone)] 6 | pub struct LogicalProjection { 7 | pub(crate) base: LogicalOperatorBase, 8 | pub(crate) table_idx: usize, 9 | } 10 | -------------------------------------------------------------------------------- /src/storage/mod.rs: -------------------------------------------------------------------------------- 1 | mod csv; 2 | mod memory; 3 | use std::io; 4 | use std::sync::Arc; 5 | 6 | use arrow::error::ArrowError; 7 | use arrow::record_batch::RecordBatch; 8 | pub use csv::*; 9 | pub use memory::*; 10 | 11 | use crate::catalog::RootCatalog; 12 | 13 | #[derive(Clone)] 14 | pub enum StorageImpl { 15 | CsvStorage(Arc), 16 | #[allow(dead_code)] 17 | InMemoryStorage(Arc), 18 | } 19 | 20 | pub trait Storage: Sync + Send + 'static { 21 | type TableType: Table; 22 | 23 | fn create_csv_table(&self, id: String, filepath: String) -> Result<(), StorageError>; 24 | 25 | fn create_mem_table(&self, id: String, data: Vec) -> Result<(), StorageError>; 26 | 27 | fn get_table(&self, id: String) -> Result; 28 | 29 | fn get_catalog(&self) -> RootCatalog; 30 | 31 | fn show_tables(&self) -> Result; 32 | } 33 | 34 | /// Optional bounds of the reader, of the form (offset, limit). 35 | type Bounds = Option<(usize, usize)>; 36 | type Projections = Option>; 37 | 38 | pub trait Table: Sync + Send + Clone + 'static { 39 | type TransactionType: Transaction; 40 | 41 | /// The bounds is applied to the whole data batches, not per batch. 42 | /// 43 | /// The projections is column indices. 44 | fn read( 45 | &self, 46 | bounds: Bounds, 47 | projection: Projections, 48 | ) -> Result; 49 | } 50 | 51 | // currently we use a transaction to hold csv reader 52 | pub trait Transaction: Sync + Send + 'static { 53 | fn next_batch(&mut self) -> Result, StorageError>; 54 | } 55 | 56 | #[derive(thiserror::Error, Debug)] 57 | pub enum StorageError { 58 | #[error("arrow error")] 59 | ArrowError(#[from] ArrowError), 60 | 61 | #[error("io error")] 62 | IoError(#[from] io::Error), 63 | 64 | #[error("table not found: {0}")] 65 | TableNotFound(String), 66 | } 67 | -------------------------------------------------------------------------------- /src/storage_v2/mod.rs: -------------------------------------------------------------------------------- 1 | mod local_storage; 2 | pub use local_storage::*; 3 | -------------------------------------------------------------------------------- /src/types_v2/errors.rs: -------------------------------------------------------------------------------- 1 | #[derive(thiserror::Error, Debug)] 2 | pub enum TypeError { 3 | #[error("invalid logical type")] 4 | InvalidLogicalType, 5 | #[error("not implemented arrow datatype: {0}")] 6 | NotImplementedArrowDataType(String), 7 | #[error("not implemented sqlparser datatype: {0}")] 8 | NotImplementedSqlparserDataType(String), 9 | #[error("internal error: {0}")] 10 | InternalError(String), 11 | } 12 | -------------------------------------------------------------------------------- /src/types_v2/mod.rs: -------------------------------------------------------------------------------- 1 | mod errors; 2 | mod types; 3 | mod values; 4 | pub use errors::*; 5 | pub use types::*; 6 | pub use values::*; 7 | -------------------------------------------------------------------------------- /tests/csv/department.csv: -------------------------------------------------------------------------------- 1 | id,department_name 2 | 1,IT 3 | 2,Marketing 4 | 3,Finance 5 | 4,Engineering 6 | -------------------------------------------------------------------------------- /tests/csv/employee.csv: -------------------------------------------------------------------------------- 1 | id,first_name,last_name,state,job_title,salary,department_id 2 | 1,Bill,Hopkins,CA,Manager,12000,1 3 | 2,Gregg,Langford,CO,Driver,10000,2 4 | 3,John,Travis,CO,"Manager, Software",11500,4 5 | 4,Von,Mill,,Defensive End,, 6 | -------------------------------------------------------------------------------- /tests/csv/state.csv: -------------------------------------------------------------------------------- 1 | id,state_code,state_name 2 | 1,CA,California State 3 | 2,CO,Colorado State 4 | 3,NJ,New Jersey 5 | 6 | -------------------------------------------------------------------------------- /tests/csv/t1.csv: -------------------------------------------------------------------------------- 1 | a,b,c 2 | 0,4,7 3 | 1,5,8 4 | 2,7,9 5 | 2,8,1 6 | -------------------------------------------------------------------------------- /tests/csv/t2.csv: -------------------------------------------------------------------------------- 1 | a,b,c 2 | 10,2,7 3 | 20,2,5 4 | 30,3,6 5 | 40,4,6 6 | -------------------------------------------------------------------------------- /tests/planner/column-pruning.yml: -------------------------------------------------------------------------------- 1 | - sql: | 2 | select a from t1 3 | desc: | 4 | PushProjectIntoTableScan: column pruning into table scan 5 | 6 | - sql: | 7 | select sum(b)+1 from t1 where a > 1 8 | desc: | 9 | PushProjectThroughChild: column pruning across aggregate 10 | 11 | - sql: | 12 | select sum(b) from t1 where a > 1 13 | desc: | 14 | RemoveNoopOperators: column pruning remove unused projection 15 | 16 | - sql: | 17 | select t1.a, t2.b from t1 left join t2 on t1.a = t2.a where t2.b > 1 18 | desc: | 19 | PushProjectThroughChild: column pruning across join 20 | 21 | - sql: | 22 | select employee.id, employee.first_name, department.department_name, state.state_name, state.state_code from employee 23 | left join department on employee.department_id=department.id 24 | right join state on state.state_code=employee.state; 25 | desc: | 26 | PushProjectThroughChild: column pruning across multiple join 27 | 28 | - sql: | 29 | select a, t2.v1 as max_b from t1 cross join (select max(b) as v1 from t1) t2 30 | desc: | 31 | PushProjectThroughChild: column pruning across subquery 32 | 33 | - sql: | 34 | select t1.a, sub0.v0, sub1.v0 from t1 cross join (select max(b) as v0 from t1) sub0 cross join (select min(b) as v0 from t1) sub1; 35 | desc: | 36 | PushProjectThroughChild: column pruning across multiple subquery 37 | 38 | - sql: | 39 | select a, (select max(b) from t1) from t1; 40 | desc: | 41 | PushProjectThroughChild: column pruning across scalar subquery 42 | 43 | - sql: | 44 | select a, (select max(b) from t1) + (select min(b) from t1) as mix_b from t1; 45 | desc: | 46 | PushProjectThroughChild: column pruning across multiple scalar subquery 47 | 48 | - sql: | 49 | select t1.a, t1.b from t1 where a >= (select max(a) from t1); 50 | desc: | 51 | PushProjectThroughChild: column pruning across scalar subquery in where expr 52 | -------------------------------------------------------------------------------- /tests/planner/combine-operators.planner.sql: -------------------------------------------------------------------------------- 1 | -- CollapseProject & CombineFilter: combine adjacent projects and filters into one 2 | 3 | select t_2.* from (select t_1.* from (select * from t1 where c < 2) t_1 where t_1.a > 1) t_2 where t_2.b > 7; 4 | 5 | /* 6 | original plan: 7 | LogicalProject: exprs [t_2.a:Nullable(Int64), t_2.b:Nullable(Int64), t_2.c:Nullable(Int64)] 8 | LogicalFilter: expr t_2.b:Nullable(Int64) > Cast(7 as Int64) 9 | LogicalProject: exprs [(t_1.a:Nullable(Int64)) as t_2.a, (t_1.b:Nullable(Int64)) as t_2.b, (t_1.c:Nullable(Int64)) as t_2.c] 10 | LogicalFilter: expr t_1.a:Nullable(Int64) > Cast(1 as Int64) 11 | LogicalProject: exprs [(t1.a:Nullable(Int64)) as t_1.a, (t1.b:Nullable(Int64)) as t_1.b, (t1.c:Nullable(Int64)) as t_1.c] 12 | LogicalFilter: expr t1.c:Nullable(Int64) < Cast(2 as Int64) 13 | LogicalTableScan: table: #t1, columns: [a, b, c] 14 | 15 | optimized plan: 16 | PhysicalProject: exprs [t_2.a:Nullable(Int64), t_2.b:Nullable(Int64), t_2.c:Nullable(Int64)] 17 | PhysicalProject: exprs [(t_1.a:Nullable(Int64)) as t_2.a, (t_1.b:Nullable(Int64)) as t_2.b, (t_1.c:Nullable(Int64)) as t_2.c] 18 | PhysicalProject: exprs [(t1.a:Nullable(Int64)) as t_1.a, (t1.b:Nullable(Int64)) as t_1.b, (t1.c:Nullable(Int64)) as t_1.c] 19 | PhysicalFilter: expr t1.b:Nullable(Int64) > 7 AND t1.a:Nullable(Int64) > 1 AND t1.c:Nullable(Int64) < 2 20 | PhysicalTableScan: table: #t1, columns: [a, b, c] 21 | */ 22 | 23 | -------------------------------------------------------------------------------- /tests/planner/combine-operators.yml: -------------------------------------------------------------------------------- 1 | - sql: | 2 | select t_2.* from (select t_1.* from (select * from t1 where c < 2) t_1 where t_1.a > 1) t_2 where t_2.b > 7; 3 | desc: | 4 | CollapseProject & CombineFilter: combine adjacent projects and filters into one 5 | -------------------------------------------------------------------------------- /tests/planner/limit-pushdown.yml: -------------------------------------------------------------------------------- 1 | - sql: | 2 | select a from t1 offset 2 limit 1 3 | desc: | 4 | LimitProjectTranspose: pushdown limit across project 5 | 6 | - sql: | 7 | select t1.a from t1 order by t1.b offset 1 limit 1 8 | desc: | 9 | PushLimitThroughJoin: don't pushdown limit when plan has order 10 | 11 | - sql: | 12 | select t1.a from t1 left join t2 on t1.a=t2.b offset 1 limit 1 13 | desc: | 14 | PushLimitThroughJoin: pushdown limit for left outer join 15 | 16 | - sql: | 17 | select t1.a from t1 right join t2 on t1.a=t2.b limit 1 18 | desc: | 19 | PushLimitThroughJoin: pushdown limit for right outer join 20 | 21 | - sql: | 22 | select t1.a from t1 right join t2 on t1.a=t2.b offset 10 23 | desc: | 24 | PushLimitThroughJoin: don't push_limit_through_join when not contains limit 25 | -------------------------------------------------------------------------------- /tests/planner/predicate-pushdown.yml: -------------------------------------------------------------------------------- 1 | - sql: | 2 | select t1.* from t1 inner join t2 on t1.a=t2.b where t2.a > 2 and t1.a > 1 3 | desc: | 4 | PushPredicateThroughJoin: pushdown to either side 5 | 6 | - sql: | 7 | select t1.* from t1 left join t2 on t1.a=t2.b where t2.a > 2 and t1.a > 1 8 | desc: | 9 | PushPredicateThroughJoin: pushdown left outer join 10 | 11 | - sql: | 12 | select t1.* from t1 right join t2 on t1.a=t2.b where t2.a > 2 and t1.a > 1 13 | desc: | 14 | PushPredicateThroughJoin: pushdown right outer join 15 | 16 | - sql: | 17 | select t1.* from t1 inner join t2 on t1.a=t2.b where t2.a > 2 and t1.a > t2.a 18 | desc: | 19 | PushPredicateThroughJoin: pushdown common filters into join condition 20 | 21 | - sql: | 22 | select t1.* from t1 left join t2 on t1.a=t2.b where t2.a > 2 and t1.a > t2.a 23 | desc: | 24 | PushPredicateThroughJoin: don't pushdown filters for left outer join 25 | 26 | - sql: | 27 | select t1.* from t1 right join t2 on t1.a=t2.b where t1.a > 2 and t1.a > t2.a 28 | desc: | 29 | PushPredicateThroughJoin: don't pushdown filters for right outer join 30 | 31 | - sql: | 32 | select t.a from (select * from t1 where a > 1) t where t.b > 7 33 | desc: | 34 | PushPredicateThroughNonJoin: pushdown filter with column alias 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /tests/slt/aggregation.slt: -------------------------------------------------------------------------------- 1 | skipif sqlrs_v2 2 | query II 3 | select sum(salary) from employee 4 | ---- 5 | 33500 6 | 7 | skipif sqlrs_v2 8 | query II 9 | select sum(salary), sum(id+1), count(id), count(salary) from employee where id > 1 10 | ---- 11 | 21500 12 3 2 12 | 13 | skipif sqlrs_v2 14 | query II 15 | select max(salary), min(id), max(last_name) from employee 16 | ---- 17 | 12000 1 Travis 18 | 19 | skipif sqlrs_v2 20 | query IIIII 21 | select salary, count(id), sum(salary), max(salary), min(salary) from employee group by salary 22 | ---- 23 | 12000 1 12000 12000 12000 24 | 10000 1 10000 10000 10000 25 | 11500 1 11500 11500 11500 26 | NULL 1 NULL NULL NULL 27 | 28 | skipif sqlrs_v2 29 | query IIIII 30 | select state, count(state), sum(salary), max(salary), min(salary) from employee group by state 31 | ---- 32 | CA 1 12000 12000 12000 33 | CO 2 21500 11500 10000 34 | (empty) 1 NULL NULL NULL 35 | 36 | skipif sqlrs_v2 37 | query IIIIII 38 | select state, id, count(state), sum(salary), max(salary), min(salary) from employee group by state, id 39 | ---- 40 | CA 1 1 12000 12000 12000 41 | CO 2 1 10000 10000 10000 42 | CO 3 1 11500 11500 11500 43 | (empty) 4 1 NULL NULL NULL 44 | -------------------------------------------------------------------------------- /tests/slt/alias.slt: -------------------------------------------------------------------------------- 1 | # expression alias 2 | skipif sqlrs_v2 3 | query I 4 | select a as c1 from t1 order by c1 desc limit 1; 5 | ---- 6 | 2 7 | 8 | skipif sqlrs_v2 9 | query I 10 | select a as c1 from t1 where c1 = 1; 11 | ---- 12 | 1 13 | 14 | skipif sqlrs_v2 15 | query II 16 | select sum(b) as c1, a as c2 from t1 group by c2 order by c1 desc; 17 | ---- 18 | 15 2 19 | 5 1 20 | 4 0 21 | 22 | # table alias 23 | skipif sqlrs_v2 24 | query I 25 | select t.a from t1 t where t.b > 1 order by t.a desc limit 1; 26 | ---- 27 | 2 28 | 29 | skipif sqlrs_v2 30 | query I 31 | select sum(t.a) as c1 from t1 as t 32 | ---- 33 | 5 34 | 35 | skipif sqlrs_v2 36 | query I 37 | select t.* from t1 t where t.b > 1 order by t.a desc limit 1; 38 | ---- 39 | 2 7 9 40 | 41 | skipif sqlrs_v2 42 | query I 43 | select t_1.a from t1 t_1 left join t2 t_2 on t_1.a=t_2.b and t_1.c > t_2.c; 44 | ---- 45 | 2 46 | 2 47 | 0 48 | 1 49 | 2 50 | 51 | # subquery alias 52 | skipif sqlrs_v2 53 | query I 54 | select t.a from (select * from t1 where a > 1) t where t.b > 7; 55 | ---- 56 | 2 57 | 58 | skipif sqlrs_v2 59 | query III 60 | select t.* from (select * from t1 where a > 1) t where t.b > 7; 61 | ---- 62 | 2 8 1 63 | 64 | skipif sqlrs_v2 65 | query I 66 | select t.v1 + 1 from (select a + 1 as v1 from t1 where a > 1) t; 67 | ---- 68 | 4 69 | 4 70 | -------------------------------------------------------------------------------- /tests/slt/comparison_function.slt: -------------------------------------------------------------------------------- 1 | onlyif sqlrs_v2 2 | statement error 3 | select 'abc' > 10 4 | 5 | onlyif sqlrs_v2 6 | statement error 7 | select 20.0 = 'abc' 8 | 9 | onlyif sqlrs_v2 10 | query T 11 | select 100 > 20 12 | ---- 13 | true 14 | 15 | onlyif sqlrs_v2 16 | query T 17 | select '1000' > '20' 18 | ---- 19 | false 20 | -------------------------------------------------------------------------------- /tests/slt/conjunction_function.slt: -------------------------------------------------------------------------------- 1 | onlyif sqlrs_v2 2 | query T 3 | SELECT true and true and true 4 | ---- 5 | true 6 | 7 | onlyif sqlrs_v2 8 | query T 9 | SELECT true and false 10 | ---- 11 | false 12 | 13 | onlyif sqlrs_v2 14 | query T 15 | SELECT false and NULL 16 | ---- 17 | false 18 | 19 | onlyif sqlrs_v2 20 | query T 21 | SELECT NULL and true 22 | ---- 23 | NULL 24 | 25 | onlyif sqlrs_v2 26 | query T 27 | SELECT true and false or false 28 | ---- 29 | false 30 | 31 | onlyif sqlrs_v2 32 | query T 33 | SELECT true or false 34 | ---- 35 | true 36 | 37 | onlyif sqlrs_v2 38 | query T 39 | SELECT false or NULL 40 | ---- 41 | NULL 42 | 43 | onlyif sqlrs_v2 44 | query T 45 | SELECT NULL or false 46 | ---- 47 | NULL 48 | 49 | 50 | # create table 51 | onlyif sqlrs_v2 52 | statement ok 53 | CREATE TABLE a (i integer, j integer); 54 | INSERT INTO a VALUES (3, 4), (4, 5), (5, 6); 55 | 56 | # test single constant in conjunctions 57 | onlyif sqlrs_v2 58 | query T 59 | SELECT true AND i>3 FROM a 60 | ---- 61 | false 62 | true 63 | true 64 | 65 | onlyif sqlrs_v2 66 | query T 67 | SELECT i>3 AND true FROM a 68 | ---- 69 | false 70 | true 71 | true 72 | 73 | onlyif sqlrs_v2 74 | query T 75 | SELECT 2>3 AND i>3 FROM a 76 | ---- 77 | false 78 | false 79 | false 80 | 81 | onlyif sqlrs_v2 82 | query T 83 | SELECT false AND i>3 FROM a 84 | ---- 85 | false 86 | false 87 | false 88 | 89 | onlyif sqlrs_v2 90 | query T 91 | SELECT i>3 AND false FROM a 92 | ---- 93 | false 94 | false 95 | false 96 | 97 | onlyif sqlrs_v2 98 | query T 99 | SELECT false OR i>3 FROM a 100 | ---- 101 | false 102 | true 103 | true 104 | 105 | onlyif sqlrs_v2 106 | query T 107 | SELECT i>3 OR false FROM a 108 | ---- 109 | false 110 | true 111 | true 112 | 113 | onlyif sqlrs_v2 114 | query T 115 | SELECT true OR i>3 FROM a 116 | ---- 117 | true 118 | true 119 | true 120 | 121 | onlyif sqlrs_v2 122 | query T 123 | SELECT i>3 OR true FROM a 124 | ---- 125 | true 126 | true 127 | true 128 | 129 | onlyif sqlrs_v2 130 | query T 131 | SELECT NULL OR i>3 FROM a 132 | ---- 133 | NULL 134 | true 135 | true 136 | 137 | onlyif sqlrs_v2 138 | query T 139 | SELECT i>3 OR NULL FROM a 140 | ---- 141 | NULL 142 | true 143 | true 144 | 145 | onlyif sqlrs_v2 146 | query T 147 | SELECT NULL AND i>3 FROM a 148 | ---- 149 | false 150 | NULL 151 | NULL 152 | 153 | onlyif sqlrs_v2 154 | query T 155 | SELECT i>3 AND NULL FROM a 156 | ---- 157 | false 158 | NULL 159 | NULL 160 | -------------------------------------------------------------------------------- /tests/slt/create_table.slt: -------------------------------------------------------------------------------- 1 | onlyif sqlrs_v2 2 | statement ok 3 | create table t1(v1 varchar, v2 varchar, v3 varchar); 4 | insert into t1 values('a', 'b', 'c'); 5 | 6 | 7 | onlyif sqlrs_v2 8 | statement error 9 | create table t1(v1 int); 10 | 11 | 12 | onlyif sqlrs_v2 13 | statement ok 14 | create table t2(v1 boolean, v2 tinyint, v3 smallint, v4 int, v5 bigint, v6 float, v7 double, v8 varchar); 15 | insert into t2 values(true, 1, 2, 3, 4, 5.1, 6.2, '7'); 16 | 17 | 18 | onlyif sqlrs_v2 19 | statement ok 20 | create table t3(v1 boolean, v2 tinyint unsigned, v3 smallint unsigned, v4 int unsigned, v5 bigint unsigned, v6 float, v7 double, v8 varchar); 21 | insert into t3 values(true, 1, 2, 3, 4, 5.1, 6.2, '7'); 22 | 23 | 24 | onlyif sqlrs_v2 25 | statement ok 26 | create table t4(v1 int); 27 | select v1 from t4; 28 | 29 | 30 | onlyif sqlrs_v2 31 | statement ok 32 | create table read_csv_table as select * from read_csv('tests/csv/t2.csv'); 33 | 34 | onlyif sqlrs_v2 35 | query I 36 | select a from read_csv_table limit 1; 37 | ---- 38 | 10 39 | -------------------------------------------------------------------------------- /tests/slt/csv/csv.slt: -------------------------------------------------------------------------------- 1 | onlyif sqlrs_v2 2 | statement ok 3 | create table state(id varchar, state_code varchar, state_name varchar); 4 | 5 | onlyif sqlrs_v2 6 | statement ok 7 | copy state from 'tests/slt/csv/state1.csv' ( DELIMITER '|' ); 8 | 9 | onlyif sqlrs_v2 10 | statement ok 11 | copy state from 'tests/slt/csv/state2.csv' ( DELIMITER '|', HEADER false); 12 | 13 | onlyif sqlrs_v2 14 | query I 15 | SELECT id FROM state 16 | ---- 17 | 1 18 | 2 19 | 3 20 | 4 21 | -------------------------------------------------------------------------------- /tests/slt/csv/state1.csv: -------------------------------------------------------------------------------- 1 | id|state_code|state_name 2 | 1|CA|California State 3 | 2|CO|Colorado State 4 | 3|NJ|New Jersey 5 | 6 | -------------------------------------------------------------------------------- /tests/slt/csv/state2.csv: -------------------------------------------------------------------------------- 1 | 4|CA|California State 2 | 3 | -------------------------------------------------------------------------------- /tests/slt/distinct.slt: -------------------------------------------------------------------------------- 1 | skipif sqlrs_v2 2 | query I 3 | select distinct state from employee; 4 | ---- 5 | CA 6 | CO 7 | (empty) 8 | 9 | skipif sqlrs_v2 10 | query II 11 | select distinct a, b from t2; 12 | ---- 13 | 10 2 14 | 20 2 15 | 30 3 16 | 40 4 17 | 18 | skipif sqlrs_v2 19 | query I 20 | select sum(distinct b) from t2; 21 | ---- 22 | 9 23 | 24 | skipif sqlrs_v2 25 | query I 26 | select sum(distinct(b)) from t2; 27 | ---- 28 | 9 29 | 30 | skipif sqlrs_v2 31 | query I 32 | select sum(distinct(b)) from t2 group by c; 33 | ---- 34 | 2 35 | 2 36 | 7 37 | 38 | skipif sqlrs_v2 39 | query I 40 | select count(distinct(b)) from t2; 41 | ---- 42 | 3 43 | -------------------------------------------------------------------------------- /tests/slt/explain.slt: -------------------------------------------------------------------------------- 1 | onlyif sqlrs_v2 2 | statement ok 3 | explain select 1, 2.3, '😇', true, null; 4 | 5 | onlyif sqlrs_v2 6 | statement ok 7 | CREATE TABLE integers(i INTEGER, j INTEGER) 8 | 9 | onlyif sqlrs_v2 10 | statement ok 11 | INSERT INTO integers VALUES (1, 1), (2, 2), (3, 3), (NULL, NULL) 12 | 13 | onlyif sqlrs_v2 14 | statement ok 15 | EXPLAIN SELECT * FROM integers 16 | -------------------------------------------------------------------------------- /tests/slt/filter.slt: -------------------------------------------------------------------------------- 1 | skipif sqlrs_v2 2 | query II 3 | select first_name from employee where id > 2 4 | ---- 5 | John 6 | Von 7 | 8 | skipif sqlrs_v2 9 | query II 10 | select id, first_name from employee where id > 2 and id < 4 11 | ---- 12 | 3 John 13 | 14 | skipif sqlrs_v2 15 | query II 16 | select id, first_name from employee where id > 3 or id = 1 17 | ---- 18 | 1 Bill 19 | 4 Von 20 | 21 | 22 | onlyif sqlrs_v2 23 | statement ok 24 | create table t1(v1 int, v2 int, v3 int); 25 | insert into t1(v3, v2, v1) values (0, 4, 1), (1, 5, 2); 26 | 27 | 28 | onlyif sqlrs_v2 29 | query III 30 | select v1, v2 from t1 where v1 >= 2 and v1 > v3; 31 | ---- 32 | 2 5 33 | 34 | # filter alias 35 | onlyif sqlrs_v2 36 | query III 37 | select v1+1 as a from t1 where a >= 2; 38 | ---- 39 | 2 40 | 3 41 | 42 | onlyif sqlrs_v2 43 | query III 44 | select v1+1 as a from t1 where a = a; 45 | ---- 46 | 2 47 | 3 48 | -------------------------------------------------------------------------------- /tests/slt/insert_table.slt: -------------------------------------------------------------------------------- 1 | # Test common insert case 2 | onlyif sqlrs_v2 3 | statement ok 4 | create table t1(v1 varchar, v2 varchar, v3 varchar); 5 | 6 | onlyif sqlrs_v2 7 | statement error 8 | insert into t1(v3) values ('0','4'); 9 | 10 | onlyif sqlrs_v2 11 | statement ok 12 | insert into t1(v3, v2) values ('0','4'), ('1','5'); 13 | 14 | onlyif sqlrs_v2 15 | statement ok 16 | insert into t1 values ('2','7','9'); 17 | 18 | onlyif sqlrs_v2 19 | query III 20 | select v1, v3, v2 from t1; 21 | ---- 22 | NULL 0 4 23 | NULL 1 5 24 | 2 9 7 25 | 26 | 27 | # Test insert value cast type 28 | onlyif sqlrs_v2 29 | statement ok 30 | create table t2(v1 int, v2 int, v3 int); 31 | 32 | onlyif sqlrs_v2 33 | statement ok 34 | insert into t2(v3, v2, v1) values (0, 4, 1), (1, 5, 2); 35 | 36 | onlyif sqlrs_v2 37 | query III 38 | select v3, v2, v1 from t2; 39 | ---- 40 | 0 4 1 41 | 1 5 2 42 | 43 | 44 | # Test insert type cast 45 | onlyif sqlrs_v2 46 | statement ok 47 | create table t3(v1 TINYINT UNSIGNED); 48 | 49 | onlyif sqlrs_v2 50 | statement error 51 | insert into t3(v1) values (1481); 52 | 53 | 54 | # Test insert null values 55 | onlyif sqlrs_v2 56 | statement ok 57 | create table t4(v1 varchar, v2 smallint unsigned, v3 bigint unsigned); 58 | 59 | onlyif sqlrs_v2 60 | statement ok 61 | insert into t4 values (NULL, 1, 2), ('', 3, NULL); 62 | 63 | onlyif sqlrs_v2 64 | statement ok 65 | insert into t4 values (NULL, NULL, NULL); 66 | 67 | onlyif sqlrs_v2 68 | query III 69 | select v1, v2, v3 from t4; 70 | ---- 71 | NULL 1 2 72 | (empty) 3 NULL 73 | NULL NULL NULL 74 | 75 | 76 | # Test insert from select 77 | onlyif sqlrs_v2 78 | statement ok 79 | CREATE TABLE integers(i INTEGER); 80 | 81 | onlyif sqlrs_v2 82 | statement ok 83 | INSERT INTO integers SELECT 42; 84 | INSERT INTO integers SELECT null; 85 | 86 | onlyif sqlrs_v2 87 | query I 88 | SELECT * FROM integers 89 | ---- 90 | 42 91 | NULL 92 | -------------------------------------------------------------------------------- /tests/slt/join_filter.slt: -------------------------------------------------------------------------------- 1 | skipif sqlrs_v2 2 | query III 3 | select employee.id, employee.first_name, employee.state, state.state_name 4 | from employee left join state on employee.state=state.state_code and state.state_name!='California State'; 5 | ---- 6 | 2 Gregg CO Colorado State 7 | 3 John CO Colorado State 8 | 1 Bill CA NULL 9 | 4 Von (empty) NULL 10 | 11 | skipif sqlrs_v2 12 | query IIIIII 13 | select t1.*, t2.* from t1 inner join t2 on t1.a=t2.b; 14 | ---- 15 | 2 7 9 10 2 7 16 | 2 8 1 10 2 7 17 | 2 7 9 20 2 5 18 | 2 8 1 20 2 5 19 | 20 | skipif sqlrs_v2 21 | query IIIIII 22 | select t1.*, t2.* from t1 inner join t2 on t1.a=t2.b and t1.c > t2.c; 23 | ---- 24 | 2 7 9 10 2 7 25 | 2 7 9 20 2 5 26 | 27 | skipif sqlrs_v2 28 | query IIIIII 29 | select t1.*, t2.* from t1 left join t2 on t1.a=t2.b; 30 | ---- 31 | 2 7 9 10 2 7 32 | 2 8 1 10 2 7 33 | 2 7 9 20 2 5 34 | 2 8 1 20 2 5 35 | 0 4 7 NULL NULL NULL 36 | 1 5 8 NULL NULL NULL 37 | 38 | skipif sqlrs_v2 39 | query IIIIII 40 | select t1.*, t2.* from t1 left join t2 on t1.a=t2.b and t1.c > t2.c; 41 | ---- 42 | 2 7 9 10 2 7 43 | 2 7 9 20 2 5 44 | 0 4 7 NULL NULL NULL 45 | 1 5 8 NULL NULL NULL 46 | 2 8 1 NULL NULL NULL 47 | 48 | skipif sqlrs_v2 49 | query IIIIII 50 | select t1.*, t2.* from t1 right join t2 on t1.a=t2.b; 51 | ---- 52 | 2 7 9 10 2 7 53 | 2 8 1 10 2 7 54 | 2 7 9 20 2 5 55 | 2 8 1 20 2 5 56 | NULL NULL NULL 30 3 6 57 | NULL NULL NULL 40 4 6 58 | 59 | skipif sqlrs_v2 60 | query IIIIII 61 | select t1.*, t2.* from t1 right join t2 on t1.a=t2.b and t1.c > t2.c; 62 | ---- 63 | 2 7 9 10 2 7 64 | 2 7 9 20 2 5 65 | NULL NULL NULL 30 3 6 66 | NULL NULL NULL 40 4 6 67 | 68 | skipif sqlrs_v2 69 | query IIIIII 70 | select t1.*, t2.* from t1 full join t2 on t1.a=t2.b; 71 | ---- 72 | 2 7 9 10 2 7 73 | 2 8 1 10 2 7 74 | 2 7 9 20 2 5 75 | 2 8 1 20 2 5 76 | NULL NULL NULL 30 3 6 77 | NULL NULL NULL 40 4 6 78 | 0 4 7 NULL NULL NULL 79 | 1 5 8 NULL NULL NULL 80 | 81 | skipif sqlrs_v2 82 | query IIIIII 83 | select t1.*, t2.* from t1 full join t2 on t1.a=t2.b and t1.c > t2.c; 84 | ---- 85 | 2 7 9 10 2 7 86 | 2 7 9 20 2 5 87 | NULL NULL NULL 30 3 6 88 | NULL NULL NULL 40 4 6 89 | 0 4 7 NULL NULL NULL 90 | 1 5 8 NULL NULL NULL 91 | 2 8 1 NULL NULL NULL 92 | -------------------------------------------------------------------------------- /tests/slt/limit.slt: -------------------------------------------------------------------------------- 1 | skipif sqlrs_v2 2 | query II 3 | select id from employee limit 2 offset 1 4 | ---- 5 | 2 6 | 3 7 | 8 | skipif sqlrs_v2 9 | query II 10 | select id from employee limit 1 offset 10 11 | ---- 12 | 13 | skipif sqlrs_v2 14 | query II 15 | select id from employee limit 0 offset 0 16 | ---- 17 | 18 | skipif sqlrs_v2 19 | query II 20 | select id from employee offset 2 21 | ---- 22 | 3 23 | 4 24 | 25 | skipif sqlrs_v2 26 | query II 27 | select id from employee limit 2 28 | ---- 29 | 1 30 | 2 31 | 32 | 33 | onlyif sqlrs_v2 34 | statement ok 35 | create table t1(v1 int, v2 int, v3 int); 36 | insert into t1(v1, v2, v3) values (0, 4, 1), (1, 5, 2), (2, 6, 3), (3, 7, 4), (4, 8, 5); 37 | 38 | onlyif sqlrs_v2 39 | query I 40 | select v1 from t1 limit 2 offset 1; 41 | ---- 42 | 1 43 | 2 44 | 45 | onlyif sqlrs_v2 46 | query I 47 | select v1 from t1 limit 1 offset 10; 48 | ---- 49 | 50 | onlyif sqlrs_v2 51 | query I 52 | select v1 from t1 limit 0 offset 0; 53 | ---- 54 | 55 | onlyif sqlrs_v2 56 | query I 57 | select v1 from t1 offset 2; 58 | ---- 59 | 2 60 | 3 61 | 4 62 | 63 | onlyif sqlrs_v2 64 | query I 65 | select v1 from t1 limit 2; 66 | ---- 67 | 0 68 | 1 69 | -------------------------------------------------------------------------------- /tests/slt/order.slt: -------------------------------------------------------------------------------- 1 | skipif sqlrs_v2 2 | query I 3 | select id from employee order by id desc offset 2 limit 1; 4 | ---- 5 | 2 6 | 7 | skipif sqlrs_v2 8 | query II 9 | select id, state from employee order by state, id desc 10 | ---- 11 | 4 (empty) 12 | 1 CA 13 | 3 CO 14 | 2 CO 15 | 16 | skipif sqlrs_v2 17 | query I 18 | select id from employee order by first_name desc offset 2 limit 1; 19 | ---- 20 | 2 21 | -------------------------------------------------------------------------------- /tests/slt/pragma.slt: -------------------------------------------------------------------------------- 1 | onlyif sqlrs_v2 2 | statement ok 3 | create table t1(v1 int, v2 int, v3 int); 4 | create table t2(v1 varchar, v2 boolean, v3 bigint); 5 | 6 | onlyif sqlrs_v2 7 | query II rowsort 8 | show tables 9 | ---- 10 | main t1 11 | main t2 12 | 13 | 14 | onlyif sqlrs_v2 15 | query II 16 | describe t1 17 | ---- 18 | t1 [v1, v2, v3] [Integer, Integer, Integer] 19 | 20 | -------------------------------------------------------------------------------- /tests/slt/scalar_function.slt: -------------------------------------------------------------------------------- 1 | onlyif sqlrs_v2 2 | statement ok 3 | CREATE TABLE test(a integer); 4 | insert into test values (1), (2), (3), (NULL); 5 | 6 | onlyif sqlrs_v2 7 | query I 8 | select a+a from test 9 | ---- 10 | 2 11 | 4 12 | 6 13 | NULL 14 | 15 | onlyif sqlrs_v2 16 | query I 17 | select a-a from test 18 | ---- 19 | 0 20 | 0 21 | 0 22 | NULL 23 | 24 | onlyif sqlrs_v2 25 | query I 26 | select a*a from test 27 | ---- 28 | 1 29 | 4 30 | 9 31 | NULL 32 | 33 | onlyif sqlrs_v2 34 | query I 35 | select a/a from test 36 | ---- 37 | 1 38 | 1 39 | 1 40 | NULL 41 | 42 | 43 | # cast arguments 44 | onlyif sqlrs_v2 45 | query I 46 | select 100 + 1000.2 47 | ---- 48 | 1100.2 49 | -------------------------------------------------------------------------------- /tests/slt/select.slt: -------------------------------------------------------------------------------- 1 | skipif sqlrs_v2 2 | query IIII 3 | select first_name, state, id, salary from employee 4 | ---- 5 | Bill CA 1 12000 6 | Gregg CO 2 10000 7 | John CO 3 11500 8 | Von (empty) 4 NULL 9 | 10 | # test insert projection with cast expression 11 | onlyif sqlrs_v2 12 | statement ok 13 | create table t2(v1 tinyint); 14 | insert into t2(v1) values (1), (5); 15 | 16 | onlyif sqlrs_v2 17 | statement ok 18 | create table t1(v1 int, v2 int, v3 int); 19 | insert into t1(v3, v2, v1) values (0, 4, 1), (1, 5, 2); 20 | 21 | 22 | onlyif sqlrs_v2 23 | query III 24 | select t1.v1, v2 from t1; 25 | ---- 26 | 1 4 27 | 2 5 28 | 29 | 30 | onlyif sqlrs_v2 31 | query III 32 | select *, t1.* from t1; 33 | ---- 34 | 1 4 0 1 4 0 35 | 2 5 1 2 5 1 36 | 37 | 38 | # TODO: use alias function to verify output column names 39 | onlyif sqlrs_v2 40 | query III 41 | select t.v1 as a, v2 as b from t1 as t; 42 | ---- 43 | 1 4 44 | 2 5 45 | 46 | 47 | onlyif sqlrs_v2 48 | query III 49 | select 1, 2.3, '😇', true, null; 50 | ---- 51 | 1 2.3 😇 true NULL 52 | -------------------------------------------------------------------------------- /tests/slt/subquery.slt: -------------------------------------------------------------------------------- 1 | # subquery as source 2 | 3 | # subquery in FROM must have an alias. same behavior as Postgres 4 | skipif sqlrs_v2 5 | statement error 6 | select * from (select * from t1 where a > 1) where b > 7; 7 | 8 | skipif sqlrs_v2 9 | query III 10 | select * from (select * from t1 where c < 2) t_1; 11 | ---- 12 | 2 8 1 13 | 14 | skipif sqlrs_v2 15 | query III 16 | select * from (select * from (select * from t1 where c < 2) t_1 where t_1.a > 1) t_2 where t_2.b > 7; 17 | ---- 18 | 2 8 1 19 | 20 | 21 | skipif sqlrs_v2 22 | query III 23 | select t.* from (select * from t1 where a > 1) t where t.b > 7; 24 | ---- 25 | 2 8 1 26 | 27 | skipif sqlrs_v2 28 | query II 29 | select t.b from (select a, b from t1 where a > 1) t where t.b > 7; 30 | ---- 31 | 8 32 | 33 | skipif sqlrs_v2 34 | query III 35 | select t_2.* from (select t_1.* from (select * from t1 where c < 2) t_1 where t_1.a > 1) t_2 where t_2.b > 7; 36 | ---- 37 | 2 8 1 38 | 39 | # scalar subquery 40 | 41 | skipif sqlrs_v2 42 | query II 43 | select a, t2.v1 as max_b from t1 cross join (select max(b) as v1 from t1) t2; 44 | ---- 45 | 0 8 46 | 1 8 47 | 2 8 48 | 2 8 49 | 50 | skipif sqlrs_v2 51 | query II 52 | select a, (select max(b) from t1) max_b from t1; 53 | ---- 54 | 0 8 55 | 1 8 56 | 2 8 57 | 2 8 58 | 59 | skipif sqlrs_v2 60 | query II 61 | select a, (select max(b) from t1) from t1; 62 | ---- 63 | 0 8 64 | 1 8 65 | 2 8 66 | 2 8 67 | 68 | skipif sqlrs_v2 69 | query II 70 | select a, (select max(b) from t1) + 2 as max_b from t1; 71 | ---- 72 | 0 10 73 | 1 10 74 | 2 10 75 | 2 10 76 | 77 | skipif sqlrs_v2 78 | query II 79 | select a, (select max(b) from t1) + (select min(b) from t1) as mix_b from t1; 80 | ---- 81 | 0 12 82 | 1 12 83 | 2 12 84 | 2 12 85 | 86 | skipif sqlrs_v2 87 | query I 88 | select t1.a, t1.b from t1 where a >= (select max(a) from t1); 89 | ---- 90 | 2 7 91 | 2 8 92 | 93 | skipif sqlrs_v2 94 | query I 95 | select t1.a, t1.b from t1 where a >= (select max(a) from t1) and b = (select max(b) from t1); 96 | ---- 97 | 2 8 98 | -------------------------------------------------------------------------------- /tests/slt/table_function.slt: -------------------------------------------------------------------------------- 1 | onlyif sqlrs_v2 2 | statement ok 3 | create table t1(v1 int, v2 int, v3 int); 4 | create table t2(v1 varchar, v2 boolean, v3 bigint); 5 | 6 | onlyif sqlrs_v2 7 | query III rowsort 8 | select schema_name, schema_oid, table_name from sqlrs_tables(); 9 | ---- 10 | main 1 t1 11 | main 1 t2 12 | 13 | onlyif sqlrs_v2 14 | query III 15 | select * from sqlrs_columns() where table_name = 't1'; 16 | ---- 17 | t1 [v1, v2, v3] [Integer, Integer, Integer] 18 | 19 | 20 | onlyif sqlrs_v2 21 | query III 22 | select column_1 from read_csv('tests/csv/t1.csv', header=>false); 23 | ---- 24 | a 25 | 0 26 | 1 27 | 2 28 | 2 29 | 30 | onlyif sqlrs_v2 31 | query III 32 | select a from read_csv('tests/csv/t1.csv', header=>true, delim=>',') where a = 1; 33 | ---- 34 | 1 35 | 36 | onlyif sqlrs_v2 37 | query III 38 | select t1.a from 'tests/csv/t1.csv'; 39 | ---- 40 | 0 41 | 1 42 | 2 43 | 2 44 | 45 | onlyif sqlrs_v2 46 | query III 47 | select tt.a from 'tests/csv/t1.csv' tt; 48 | ---- 49 | 0 50 | 1 51 | 2 52 | 2 53 | -------------------------------------------------------------------------------- /tests/slt/time.slt: -------------------------------------------------------------------------------- 1 | onlyif sqlrs_v2 2 | statement ok 3 | create table t5(v1 date); 4 | insert into t5 values ('2021-01-02'), ('2021-01-03'); 5 | 6 | onlyif sqlrs_v2 7 | query I 8 | select v1 + interval '1' day from t5; 9 | ---- 10 | 2021-01-03 11 | 2021-01-04 12 | 13 | onlyif sqlrs_v2 14 | query I 15 | select interval '1' year + date '1998-12-01'; 16 | ---- 17 | 1999-12-01 18 | 19 | onlyif sqlrs_v2 20 | query I 21 | select interval '1' month + date '1998-12-01'; 22 | ---- 23 | 1999-01-01 24 | 25 | onlyif sqlrs_v2 26 | query I 27 | select date '1998-12-01' - interval '1' month; 28 | ---- 29 | 1998-11-01 30 | 31 | onlyif sqlrs_v2 32 | query I 33 | select date '1998-12-01' - interval '1' day; 34 | ---- 35 | 1998-11-29 36 | -------------------------------------------------------------------------------- /tests/sqllogictest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sqllogictest-test" 3 | version = "0.4.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | sqlrs = { path = "../.." } 8 | sqllogictest = "0.6" 9 | glob = "0.3" 10 | async-trait = "0.1" 11 | libtest-mimic = "0.6" 12 | 13 | [[test]] 14 | name = "sqllogictest" 15 | harness = false 16 | -------------------------------------------------------------------------------- /tests/sqllogictest/README.md: -------------------------------------------------------------------------------- 1 | ## Convention 2 | 3 | sqllogictest will read all csv files in `../csv` directory, and then create tables based on filename. 4 | -------------------------------------------------------------------------------- /tests/sqllogictest/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(iterator_try_collect)] 2 | 3 | use std::sync::Arc; 4 | 5 | use sqllogictest::{AsyncDB, Runner}; 6 | use sqlrs::db::{Database, DatabaseError}; 7 | use sqlrs::main_entry::{ClientContext, DatabaseError as DatabaseErrorV2, DatabaseInstance}; 8 | use sqlrs::util::record_batch_to_string; 9 | 10 | fn init_tables(db: Arc) { 11 | const CSV_FILES: &str = "tests/csv/**/*.csv"; 12 | 13 | let csv_files = glob::glob(CSV_FILES).expect("failed to find csv files"); 14 | for csv_file in csv_files { 15 | let filepath = csv_file.expect("failed to read csv file"); 16 | let filename = filepath.file_stem().expect("failed to get file name"); 17 | let filepath = filepath.to_str().unwrap(); 18 | let filename = filename.to_str().unwrap(); 19 | db.create_csv_table(filename.into(), filepath.into()) 20 | .expect("failed to create table"); 21 | } 22 | } 23 | 24 | pub fn test_run(sqlfile: &str) { 25 | let db = Arc::new(Database::new_on_csv()); 26 | init_tables(db.clone()); 27 | println!("init database with csv tables done for {}", sqlfile); 28 | 29 | let mut tester = Runner::new(DatabaseWrapper { db }); 30 | tester.run_file(sqlfile).unwrap() 31 | } 32 | 33 | struct DatabaseWrapper { 34 | db: Arc, 35 | } 36 | 37 | #[async_trait::async_trait] 38 | impl AsyncDB for DatabaseWrapper { 39 | type Error = DatabaseError; 40 | async fn run(&mut self, sql: &str) -> Result { 41 | let chunks = self.db.run(sql).await?; 42 | let output = chunks.iter().map(record_batch_to_string).try_collect(); 43 | Ok(output?) 44 | } 45 | } 46 | 47 | struct DatabaseWrapperV2 { 48 | client_context: Arc, 49 | } 50 | 51 | #[async_trait::async_trait] 52 | impl AsyncDB for DatabaseWrapperV2 { 53 | type Error = DatabaseErrorV2; 54 | 55 | async fn run(&mut self, sql: &str) -> Result { 56 | let chunks = self.client_context.query(sql.to_string()).await?; 57 | let output = chunks.iter().map(record_batch_to_string).try_collect(); 58 | Ok(output?) 59 | } 60 | 61 | fn engine_name(&self) -> &str { 62 | "sqlrs_v2" 63 | } 64 | } 65 | 66 | pub fn test_run_v2(sqlfile: &str) { 67 | let dbv2 = Arc::new(DatabaseInstance::default()); 68 | dbv2.initialize().unwrap(); 69 | let client_context = ClientContext::new(dbv2); 70 | let mut tester = Runner::new(DatabaseWrapperV2 { client_context }); 71 | tester.run_file(sqlfile).unwrap() 72 | } 73 | -------------------------------------------------------------------------------- /tests/sqllogictest/tests/sqllogictest.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use libtest_mimic::{Arguments, Trial}; 4 | use sqllogictest_test::{test_run, test_run_v2}; 5 | 6 | fn main() { 7 | let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("..").join(".."); 8 | std::env::set_current_dir(path).unwrap(); 9 | 10 | const SLT_PATTERN: &str = "tests/slt/**/*.slt"; 11 | 12 | let args = Arguments::from_args(); 13 | let mut tests = vec![]; 14 | 15 | let slt_files = glob::glob(SLT_PATTERN).expect("failed to find slt files"); 16 | for slt_file in slt_files { 17 | let filepath = slt_file.expect("failed to read slt file"); 18 | let filename = filepath 19 | .file_stem() 20 | .expect("failed to get file name") 21 | .to_str() 22 | .unwrap() 23 | .to_string(); 24 | let filepath = filepath.to_str().unwrap().to_string(); 25 | 26 | let test = Trial::test(filename, move || { 27 | test_run(filepath.as_str()); 28 | test_run_v2(filepath.as_str()); 29 | Ok(()) 30 | }); 31 | 32 | tests.push(test); 33 | } 34 | 35 | libtest_mimic::run(&args, tests).exit(); 36 | } 37 | -------------------------------------------------------------------------------- /tests/sqlplannertest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sqlplannertest-test" 3 | version = "0.4.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | sqlrs = { path = "../.." } 8 | sqlplannertest = { git = "https://github.com/risinglightdb/sqlplannertest-rs" } 9 | glob = "0.3" 10 | async-trait = "0.1" 11 | anyhow = "1" 12 | tokio = "1" 13 | 14 | [[test]] 15 | name = "plannertest" 16 | harness = false 17 | -------------------------------------------------------------------------------- /tests/sqlplannertest/README.md: -------------------------------------------------------------------------------- 1 | ## Convention 2 | 3 | sqlplannertest will read all csv files in `../csv` directory, and then create tables based on filename. 4 | -------------------------------------------------------------------------------- /tests/sqlplannertest/src/bin/apply.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use anyhow::Result; 4 | use sqlplannertest_test::DatabaseWrapper; 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<()> { 8 | sqlplannertest::planner_test_apply( 9 | Path::new(env!("CARGO_MANIFEST_DIR")) 10 | .join("..") 11 | .join("planner"), 12 | || async { Ok(DatabaseWrapper::new("tests/csv/**/*.csv")) }, 13 | ) 14 | .await?; 15 | Ok(()) 16 | } 17 | -------------------------------------------------------------------------------- /tests/sqlplannertest/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(iterator_try_collect)] 2 | 3 | use std::sync::Arc; 4 | 5 | use anyhow::Error; 6 | use sqlplannertest::{ParsedTestCase, PlannerTestRunner}; 7 | use sqlrs::db::Database; 8 | 9 | fn init_tables(db: Arc, csv_path: &str) { 10 | let csv_files = glob::glob(csv_path).expect("failed to find csv files"); 11 | for csv_file in csv_files { 12 | let filepath = csv_file.expect("failed to read csv file"); 13 | let filename = filepath.file_stem().expect("failed to get file name"); 14 | let filepath = filepath.to_str().unwrap(); 15 | let filename = filename.to_str().unwrap(); 16 | println!("create table {} from {}", filename, filepath); 17 | db.create_csv_table(filename.into(), filepath.into()) 18 | .expect("failed to create table"); 19 | } 20 | } 21 | 22 | pub struct DatabaseWrapper { 23 | db: Arc, 24 | } 25 | 26 | impl DatabaseWrapper { 27 | pub fn new(csv_path: &str) -> Self { 28 | let db = Arc::new(Database::new_on_csv()); 29 | init_tables(db.clone(), csv_path); 30 | Self { db } 31 | } 32 | } 33 | 34 | #[async_trait::async_trait] 35 | impl PlannerTestRunner for DatabaseWrapper { 36 | async fn run(&mut self, test_case: &ParsedTestCase) -> Result { 37 | let explain_str = self.db.explain(&test_case.sql).await?; 38 | Ok(explain_str) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/sqlplannertest/tests/plannertest.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use anyhow::Result; 4 | use sqlplannertest_test::DatabaseWrapper; 5 | 6 | fn main() -> Result<()> { 7 | sqlplannertest::planner_test_runner( 8 | Path::new(env!("CARGO_MANIFEST_DIR")) 9 | .join("..") 10 | .join("planner"), 11 | || async { Ok(DatabaseWrapper::new("../csv/**/*.csv")) }, 12 | )?; 13 | Ok(()) 14 | } 15 | --------------------------------------------------------------------------------